微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何使调度调用在 useEffect 中同步?

如何解决如何使调度调用在 useEffect 中同步?

我正在尝试使用 CoreUI 表组件对来自我的其余服务器的数据进行分页。 在 useEffect 中发出调度请求后,我无法从 redux 存储中获取更新的数据,我正在使用 redux thunk,我知道调度是异步的,但是有没有办法等待调度完成?我厌倦了让调度成为 Promise 但它没有用。

我成功地从 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

解决方法

ProductsTable 始终具有先前状态数据的原因是因为您用于更新 ProductsTable 的效果缺少 store 作为依赖项或更具体地说是 store.objects.results;当 pagefetchTrigger 更改时,效果变得陈旧,因为它不知道当这些依赖项更改时效果应该更改。

  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]);

调度被多次调用,因为您有一个递归情况,其中 fetchTrigger 是效果的依赖项,但您也从效果内更新它。通过删除该依赖项,您会看到更少的调用此效果,即仅在页面更改时。我不知道您需要该值用于什么,因为我没有看到您共享的代码中使用了它,但是如果您确实需要它,我建议您使用 callback 版本的 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
  },store.objects.totalPages]);

解释完这些问题后,您最好不要为您的 itemspagesloading 添加新状态,而是从您的 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}
    />
  );

但是对于初始结果,您仍然需要某种方式来获取,您可以使用仅在安装组件时运行一次的效果来执行此操作。应该这样做,因为调度不应改变。

  // on mount lets get the initial results
  useEffect(() => {
    dispatch(
      getAllProducts({
        params: {
          page: 1,})
    );
  },[dispatch]);

与推荐的更改一起看起来像这样:

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;

,

首先,你不需要任何同步来实现你想要的结果,你必须切换你的代码,这样它就不会使用反应状态,因为你已经在使用某种全局存储(我假设还原 );您需要做的是直接从商店中获取所有物品,不要在组件上做额外的逻辑(阅读关注点分离);此外,我建议在服务器端进行分页,而不仅仅是在前端对数据进行分页。 (getAllProducts() 方法打开只获取一页结果而不是所有产品);您的代码有很多调度,因为您使用 page 和 fetchTrigger 作为 useEffect 钩子的依赖项,这意味着每次页面或 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',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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。