Javascript 如何使分派呼叫在useffect中同步?
我正在尝试使用CoreUI表组件对rest服务器中的数据进行分页。 在useffect中发出调度请求后,我无法从redux存储中获取更新的数据,我使用的是redux thunk,我知道调度是异步的,但是否有办法等待调度完成?我努力使派遣成为一个承诺,但它没有起作用 我成功地从action和reducer获得了更新的结果,但在ProductsTable中,我检查了redux devtools扩展,可以看到状态正在更改 我从未从商店得到最新的价值 此外,调度被调用的次数太多了,我可以在控制台窗口中看到,它不是一个无限循环,它会在一段时间后停止Javascript 如何使分派呼叫在useffect中同步?,javascript,reactjs,redux,redux-thunk,core-ui,Javascript,Reactjs,Redux,Redux Thunk,Core Ui,我正在尝试使用CoreUI表组件对rest服务器中的数据进行分页。 在useffect中发出调度请求后,我无法从redux存储中获取更新的数据,我使用的是redux thunk,我知道调度是异步的,但是否有办法等待调度完成?我努力使派遣成为一个承诺,但它没有起作用 我成功地从action和reducer获得了更新的结果,但在ProductsTable中,我检查了redux devtools扩展,可以看到状态正在更改 我从未从商店得到最新的价值 此外,调度被调用的次数太多了,我可以在控制台窗口中看
const ProductsTable = (props) => {
const store = useSelector((state) => state.store);
const dispatch = useDispatch();
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(true);
const [page, setPage] = useState(1);
const [pages, setPages] = useState(1);
const [itemsPerPage, setItemsPerPage] = useState(5);
const [fetchTrigger, setFetchTrigger] = useState(0);
useEffect(() => {
setLoading(true);
const payload = {
params: {
page,
},
};
if (page !== 0)
dispatch(getAllProducts(payload));
console.log("runs:" + page)
console.log(store.objects)
if(!(Object.keys(store.objects).length === 0)){
setItems(store.objects.results)
setPages(store.objects.total.totalPages)
setLoading(false)
} else{
console.log("error")
setFetchTrigger(fetchTrigger + 1);
}
}, [page, fetchTrigger]);
return (
<CCard className="p-5">
<CDataTable
items={items}
fields={["title", "slug", {
key: 'show_details',
label: '',
_style: { width: '1%' },
sorter: false,
filter: false
}]}
loading={loading}
hover
cleaner
sorter
itemsPerPage={itemsPerPage}
onPaginationChange={setItemsPerPage}
<CPagination
pages={pages}
activePage={page}
onActivePageChange={setPage}
className={pages < 2 ? "d-none" : ""}
/>
</CCard>
)
}
export default ProductsTable
constproductstable=(道具)=>{
常量存储=使用选择器((状态)=>state.store);
const dispatch=usedpatch();
const[items,setItems]=useState([]);
const[loading,setLoading]=useState(true);
const[page,setPage]=useState(1);
const[pages,setPages]=useState(1);
const[itemsPerPage,setItemsPerPage]=useState(5);
常量[fetchTrigger,setFetchTrigger]=useState(0);
useffect(()=>{
设置加载(真);
常数有效载荷={
参数:{
页
},
};
如果(第!==0页)
调度(获取所有产品(有效载荷));
日志(“运行:”+页面)
console.log(store.objects)
if(!(Object.keys(store.objects.length==0)){
setItems(store.objects.results)
setPages(store.objects.total.totalPages)
设置加载(错误)
}否则{
console.log(“错误”)
setFetchTrigger(fetchTrigger+1);
}
},[page,fetchTrigger]);
返回(
首先,你不需要任何同步来实现你想要的结果,你必须切换你的代码,这样它就不会使用react的状态,因为你已经在使用某种全局存储(我假设redux);你需要做的是直接从存储中获取所有的项,而不要在组件上做额外的逻辑(阅读以分离关注点);我还建议在服务器端进行分页,而不仅仅是在前端对数据进行分页。(getAllProducts()方法用于切换仅获取一页结果而不是所有产品);您的代码有很多分派,因为您使用page和fetchTrigger作为useEffect挂钩的依赖项,这意味着每次page或fetchTrigger值更改时,useEffect中的代码都将再次运行,从而导致另一个分派
这是代码的一个稍微修改的部分,您需要在您的操作中添加一些额外的内容,并在全局状态中添加一个加载参数
const ProductsTable = (props) => {
const dispatch = useDispatch();
// PUT THE LOADING IN THE GLOBAL STATE OR HANDLE IT VIA A CALLBACK OR ADD A GLOBAL MECHANISM TO HANLE LOADINGS INSIDE THE APP
const loading = useSelector(() => state.store.loading)
const items = useSelector((state) => state.store.objects?.results); // ADD DEFAULT EMPTY VALUES FOR objects smthg like : { objects: { results: [], total: { totalPages: 0 } }}
const pages = useSelector((state) => state.store.objects?.total?.totalPages);
const [page, setPage] = useState(1);
const [itemsPerPage, setItemsPerPage] = useState(5);
const [fetchTrigger, setFetchTrigger] = useState(0); // I DONT UNDERSTAND THIS ONE
const fetchData = () => dispatch(getAllProducts({ params: { page }}));
useEffect(() => {
fetchData();
}, []);
return (
<CCard className="p-5">
<CDataTable
items={items}
fields={["title", "slug", {
key: 'show_details',
label: '',
_style: { width: '1%' },
sorter: false,
filter: false
}]}
loading={loading}
hover
cleaner
sorter
itemsPerPage={itemsPerPage}
onPaginationChange={setItemsPerPage}
<CPagination
pages={pages}
activePage={page}
onActivePageChange={setPage}
className={pages < 2 ? "d-none" : ""}
/>
</CCard>
)
}
export default ProductsTable
constproductstable=(道具)=>{
const dispatch=usedpatch();
//将加载置于全局状态,或通过回调进行处理,或向应用程序内的HANLE加载添加全局机制
常量加载=使用选择器(()=>state.store.load)
const items=useSelector((state)=>state.store.objects?.results);//为对象smthg添加默认空值,如:{objects:{results:[],total:{totalPages:0}}
const pages=useSelector((state)=>state.store.objects?.total?.totalPages);
const[page,setPage]=useState(1);
const[itemsPerPage,setItemsPerPage]=useState(5);
const[fetchTrigger,setFetchTrigger]=useState(0);//我不明白这个
const fetchData=()=>dispatch(getAllProducts({params:{page}));
useffect(()=>{
fetchData();
}, []);
返回(
ProductsTable
始终具有以前的状态数据的原因是,用于更新ProductsTable
的效果缺少作为依赖项的store
,或者更具体地说是store.objects.results
;当页面
和fetchTrigger
更改时,效果变为陈旧因为它不知道当这些依赖项改变时,效果应该改变
useEffect(() => {
// store.objects is a dependency that is not tracked
if (!(Object.keys(store.objects).length === 0)) {
// store.objects.results is a dependency that is not tracked
setItems(store.objects.results);
// store.objects.total.totalPages is a dependency that is not tracked
setPages(store.objects.total.totalPages);
setLoading(false);
}
// add these dependencies to the effect so that everything works as expected
// avoid stale closures
}, [page, fetchTrigger, store.objects, store.objects.results, store.objects.total.totalPages]);
调用dispatch的次数很多,因为在递归情况下,fetchTrigger
是效果的依赖项,但您也会从效果中更新它。通过删除该依赖项,您将看到对该效果的调用要少得多,即仅当页面更改时。我不知道您需要该值做什么,因为我不知道在您共享的代码中可以看到它的使用,但是如果您确实需要它,我建议使用setState的回调
版本,这样您就可以引用所需的fetchTrigger
的值,而无需将其作为依赖项添加
useEffect(() => {
// code
if (!(Object.keys(store.objects).length === 0)) {
// code stuffs
} else {
// use the callback version of setState to get the previous/current value of fetchTrigger
// so you can remove the dependency on the fetchTrigger
setFetchTrigger(fetchTrigger => fetchTrigger + 1);
}
// remove fetchTrigger as a dependency
}, [page, store.objects, store.objects.results, store.objects.totalPages]);
有了这些问题的解释,您最好不要为项目
、页面
、或加载
添加新状态,而是从您的redux存储中获取新状态,因为看起来就是这样
const items = useSelector((state) => state.store.objects?.results);
const pages = useSelector((state) => state.store.objects?.total?.totalPages);
const loading = useSelector((state) => !Object.keys(state.store.objects).length === 0);
并完全删除该效果,以支持添加到onActivePageChange
事件中的函数
const onActivePageChange = page => {
setPage(page);
setFetchTrigger(fetchTrigger => fetchTrigger + 1);
dispatch(getAllProducts({
params: {
page,
},
}));
};
return (
<CPagination
// other fields
onActivePageChange={onActivePageChange}
/>
);
加上建议的更改,看起来是这样的:
const ProductsTable = props => {
const items = useSelector(state => state.store.objects?.results);
const pages = useSelector(state => state.store.objects?.total?.totalPages);
const loading = useSelector(state => !Object.keys(state.store.objects).length === 0);
const dispatch = useDispatch();
const [page, setPage] = useState(1);
const [itemsPerPage, setItemsPerPage] = useState(5);
const [fetchTrigger, setFetchTrigger] = useState(0);
// on mount lets get the initial results
useEffect(() => {
dispatch(
getAllProducts({
params: {
page: 1,
},
})
);
},[dispatch]);
const onActivePageChange = page => {
setPage(page);
setFetchTrigger(fetchTrigger => fetchTrigger + 1);
dispatch(
getAllProducts({
params: {
page,
},
})
);
};
return (
<CCard className="p-5">
<CDataTable
items={items}
fields={[
'title',
'slug',
{
key: 'show_details',
label: '',
_style: { width: '1%' },
sorter: false,
filter: false,
},
]}
loading={loading}
hover
cleaner
sorter
itemsPerPage={itemsPerPage}
onPaginationChange={setItemsPerPage}
/>
<CPagination
pages={pages}
activePage={page}
onActivePageChange={onActivePageChange}
className={pages < 2 ? 'd-none' : ''}
/>
</CCard>
);
};
export default ProductsTable;
constproductstable=props=>{
const items=useSelector(state=>state.store.objects?.results);
const pages=useSelector(state=>state.store.objects?.total?.totalPages);
常量加载=useSelector(state=>!Object.keys(state.store.objects).length==0);
const dispatch=usedpatch();
const[page,setPage]=useState(1);
const[itemsPerPage,setItemsPerPage]=useState(5);
常量[fetchTrigger,setFetchTrigger]=useState(0);
//在mount上,让我们获得初始结果
useffect(()=>{
派遣(
getAllProducts({
参数:{
页码:1,
},
})
);
},[发送];
const onActivePageChange=页面=>{
设置页面(第页);
setFetchTrigger(fetchTrigger=>fetchTrigger+1);
派遣(
getAllProducts({
参数:{
页
},
})
);
};
返回(
);
};
导出默认值P
const ProductsTable = props => {
const items = useSelector(state => state.store.objects?.results);
const pages = useSelector(state => state.store.objects?.total?.totalPages);
const loading = useSelector(state => !Object.keys(state.store.objects).length === 0);
const dispatch = useDispatch();
const [page, setPage] = useState(1);
const [itemsPerPage, setItemsPerPage] = useState(5);
const [fetchTrigger, setFetchTrigger] = useState(0);
// on mount lets get the initial results
useEffect(() => {
dispatch(
getAllProducts({
params: {
page: 1,
},
})
);
},[dispatch]);
const onActivePageChange = page => {
setPage(page);
setFetchTrigger(fetchTrigger => fetchTrigger + 1);
dispatch(
getAllProducts({
params: {
page,
},
})
);
};
return (
<CCard className="p-5">
<CDataTable
items={items}
fields={[
'title',
'slug',
{
key: 'show_details',
label: '',
_style: { width: '1%' },
sorter: false,
filter: false,
},
]}
loading={loading}
hover
cleaner
sorter
itemsPerPage={itemsPerPage}
onPaginationChange={setItemsPerPage}
/>
<CPagination
pages={pages}
activePage={page}
onActivePageChange={onActivePageChange}
className={pages < 2 ? 'd-none' : ''}
/>
</CCard>
);
};
export default ProductsTable;