我有一个问题,从列表中删除元素后,组件不想重新渲染。当我通过路由到某个子页面然后返回时,项目已经被删除,如果我对列表进行排序,那么它也会消失。问题是我不想强迫用户刷新网站或排序才能看到效果。如果我转到
,它曾经有效deviceList 道具作为“props.data”而不是“devices”
(将代码中的位置加粗)。有谁知道如何解决它?我对 React hooks 还比较陌生,也许我只是需要更好地调整 useEffect , props.selectedDevices 取自 redux,仅获取已删除项目的 ID,我的代码:
家长:
function App(props) {
const [data, setData] = useState("")
const [filter, setFilter] = useState([])
useEffect(() => getDeviceDataTotal(), [])
const getDeviceDataTotal = () => {
console.log('refresh clicked')
fetch("http://localhost:4000/device")
.then(res => res.json())
.then(res => setData(res))
.catch(err => {
throw err;
});
};
const deleteDevices = (e) => {
console.log('delete clicked')
props.selectedDevices.map(el => {
return fetch(`http://localhost:4000/device/delete/${el}`, {
method: "DELETE"
})
.then(res => res.json())
.then(() => getDeviceDataTotal())
.catch(err => {
throw err;
});
});
}
const filterList = e => {
let filteredList;
filteredList = data.filter(el => {
return el.device_id.includes(searched);
});
setFilter(filteredList)
};
return (
<BrowserRouter>
<MuiThemeProvider theme={createMuiTheme(theme("#96BF1B", "#ffffff"))}>
<Switch>
<Route
exact
path="/"
render={() => <Dashboard classes={classes} dataDevice={data} refresh={getDeviceDataTotal} filtered={filter} filterList={filterList} deleteDevices={deleteDevices}/> }
/>
......
</Switch>
</MuiThemeProvider>
</BrowserRouter>
);
}
子组件:
function Dashboard(props) {
const [selectedSort, setSelectedSort] = useState("1")
const [devices, setDevices] = useState(props.dataDevice)
useEffect(() => {
let isSubscribed = true
const fetchData = async() => {
try {
const res = await fetch("http://localhost:4000/device")
const response = await res.json()
if (isSubscribed) {
setDevices(response)
}
} catch (err) {
console.log(err)
}
}
fetchData()
return () => isSubscribed = false
},[])
useEffect(() => props.refresh() ,[devices])
useEffect(() => sortDeviceData(), [selectedSort])
const sortDeviceData = () => {
switch(selectedSort) {
case "device":
(filtered.length ? filtered : dataDevice).sort(function(a,b) {
return (a.device_id.toUpperCase() < b.device_id.toUpperCase()) ? -1 : (a.device_id.toUpperCase() > b.device_id.toUpperCase()) ? 1 : 0
})
setDevices(dataDevice)
break;
case "customer":
(filtered.length ? filtered : dataDevice).sort(function(a,b) {
return (a.customer.toUpperCase() < b.customer.toUpperCase()) ? -1 : (a.customer.toUpperCase() > b.customer.toUpperCase()) ? 1 : 0
})
setDevices(dataDevice)
break;
case "server":
(filtered.length ? filtered : dataDevice).sort(function(a,b) {
return (a.server.toUpperCase() < b.server.toUpperCase()) ? -1 : (a.server.toUpperCase() > b.server.toUpperCase()) ? 1 : 0
})
setDevices(dataDevice)
break;
case "creation":
(filtered.length ? filtered : dataDevice).sort(function(a,b) {
return (a.createdAt.toUpperCase() < b.createdAt.toUpperCase()) ? -1 : (a.createdAt.toUpperCase() > b.createdAt.toUpperCase()) ? 1 : 0
})
setDevices(dataDevice)
break;
case "update":
(filtered.length ? filtered : dataDevice).sort(function(a,b) {
return (a.updatedAt.toUpperCase() < b.updatedAt.toUpperCase()) ? -1 : (a.updatedAt.toUpperCase() > b.updatedAt.toUpperCase()) ? 1 : 0
})
setDevices(dataDevice)
break;
default:
return setDevices(dataDevice)
}
}
const { classes, logged, dataDevice, filtered } = props;
return logged ? (
<main style={{ display: "flex" }} key={dataDevice}>
<section className={classes.sectionLeftContainer}>
<div className={classNames(classes.search, classes.tableBackground)}>
<InputBase
placeholder="Search…"
classes={{
root: classes.inputRoot,
input: classes.inputInput
}}
inputProps={{ "aria-label": "Search" }}
onChange={e => props.getSearched(e.target.value)}
/>
<Button
variant="outlined"
className={classes.searchBtn}
onClick={e => props.filterList(e)}
>
Search
</Button>
<Button
variant="outlined"
className={classes.btnAddDevice}
onClick={e => {
if (window.confirm("Are you sure you want to delete this device")) {
props.deleteDevices(e);
}
}}
>
<DeleteIcon />
Delete devices
</Button>
</div>
<div>
<FormControl className={classes.selectContainer}>
<Select
value={selectedSort}
style={{position: 'absolute', right: 0, bottom: 10, width: 150}}
onChange={e => setSelectedSort(e.target.value)}
>
<MenuItem value="1">Default</MenuItem>
<MenuItem value="device">Device</MenuItem>
<MenuItem value="customer">User</MenuItem>
<MenuItem value="server">Server</MenuItem>
<MenuItem value="creation">Creation date</MenuItem>
<MenuItem value="update">Update date</MenuItem>
</Select>
</FormControl>
</div>
<div>
<DeviceList
classes={classes}
**deviceData={devices} if props.data - works well but sorting doesn't work**
filtered={filtered}
/>
</div>
</section>
</main>
}
强制 React 组件渲染的方法有多种,但本质上是相同的。 第一个是使用
this.forceUpdate()
,它会跳过 shouldComponentUpdate
:
someMethod() {
// Force a render without state change...
this.forceUpdate();
}
假设您的组件有状态,您还可以调用以下命令:
someMethod() {
// Force a render with a simulated state change
this.setState({ state: this.state });
}
可能有一种更好、更“React-y”的方式来正确渲染组件,但如果您迫切希望根据命令渲染组件,那么这就可以了。
您应该将此删除功能拉至保存设备列表的父组件。然后将该函数作为 props 向下传递,并从具有要删除的设备的子组件中调用它。
const deleteDevices = (e) => {
return fetch(`http://localhost:4000/device/delete/${e}`, {
method: "DELETE"
})
.then(res => res.json())
.then(_ => setDevice(devices.filter(device => device !== e)))
.catch(err => {
throw err;
});
}
将获取逻辑分离到一个函数中,并在删除请求后调用它,以便更新数据。
const getData = () => {
// fetch data
}
useEffect(() => {
getData()
}, [])
const deleteData = () => {
fetch(...).then(() => {
getData()
})
}
尝试更新您的父组件 useEffect 挂钩。
//...
const getDeviceDataTotal = () => {
// Api call...
};
// Reference the getDeviceDataTotal function inside the hook.
useEffect(getDeviceDataTotal, []);
//...
或者将您的 api 调用函数包装在 useCallback 挂钩中,并将其作为依赖项包含在 useEffect 挂钩中。
//...
const getDeviceDataTotal = useCallback(() => {
// Api call...
},[]);
// Include getDeviceDataTotal as a dependency.
useEffect(() => getDeviceDataTotal(), [getDeviceDataTotal]);
//...