【问题标题】:Updating state with fetch then using state in useEffect doesn't work使用 fetch 更新状态,然后在 useEffect 中使用状态不起作用
【发布时间】:2020-02-13 14:31:26
【问题描述】:

我在 React 中有一个组件,在 useEffect 中我进行了两次 API 调用并设置了两种状态。仍然在useEffect 之后,我试图设置另一个状态,其中一个状态由 API 更新。如果我通过保持useEffect 的方括号参数为空来执行此操作,则不会设置状态。如果我传递括号中的状态常量,它会进入无限循环。

async function fetchData() {
    const agentRes = await fetch("http://localhost:3004/agentsdata");
        agentRes
            .json()
            .then(res => setAgents(res))
            .catch(err => console.log(err));

    const queueRes = await fetch("http://localhost:3004/queuedata");
        queueRes
            .json()
            .then(res => setBaseQueues(res))
            .catch(err => console.log(err));
    }

useEffect(() => {

    fetchData();

    let pge;

    if (props.location.data !== undefined) {
        pge = props.location.data.page;
    }
    else {pge = 1;}

    setUser(props.match.params.user);
    setPage(pge);


    let lowerB = (0 +((pge - 1) * 10));
    let upperB = 10 + ((pge - 1) * 10);

    setQueues(baseQueues.slice(lowerB, upperB)); // here the state is not getting set.
}, [queues])

我如何做到这一点,以便我可以设置该状态以及我需要在 useEffect 中进行设置。

【问题讨论】:

    标签: javascript reactjs react-hooks setstate


    【解决方案1】:

    useEffect(() => {
      const doAsyncStuff = async () => {
         await fetchData(); // wait for this data to load
         const { data } = props.location;
         let pge = data !== undefined ? data.page : 1;
         setUser(props.match.params.user);
         setPage(pge);
         
         let lowerB = (0 +((pge - 1) * 10));
         let upperB = 10 + ((pge - 1) * 10);
         
         setQueues(state => state.slice(lowerB, upperB)); // change here
      }
      doAsyncStuff();
    }, [queues])

    首先我认为你需要等待数据加载,所以你必须等待await fetchData();

    然后在设置setQueues 时,您必须执行setQueues(state => state.slice(lowerB, upperB));

    【讨论】:

      【解决方案2】:

      您可以使用更简单的化学式:

      useEffect(() => {
        fetchData();
      }, [queues])
      
      useEffect(() => {
        let pge;
      
        if (props.location.data !== undefined) {
            pge = props.location.data.page;
        }
        else {pge = 1;}
      
        setUser(props.match.params.user);
        setPage(pge);
      
        let lowerB = (0 +((pge - 1) * 10));
        let upperB = 10 + ((pge - 1) * 10);
      
        setQueues(baseQueues.slice(lowerB, upperB)); // here baseQueues is updated.
      }, [baseQueues])
      

      ... 但您可能不需要大部分内容

      • queues 是派生数据;
      • 可以在页面属性更改后的每次渲染上“计算”该子集;
      • 获取页面参数后可能超出范围 - 重定向?

      【讨论】:

      • 这个答案部分正确,我从第一个 useEffect 的第二个参数中删除了队列,它工作正常。
      • 全部取决于使用上下文,整个组件 - useEffects 中的空数组只触发一次(如 componentDidMount) - 在需要重新获取时在此处放置一个道具/变量
      • 请注意,这种实现是有问题的,因为它捕获了陈旧的闭包。请注意,您的效果取决于props.match.params.user 和props.location.data.page,因此它们应该包含在依赖数组中。 props.location.data.page 中的任何更改都不会被导致 baseQueues 状态中的陈旧数据的效果处理。
      • @chitova263 original 不知道props.location.data.page 的变化,也...您可以返回从原始fetchData() 获取的数据...我们正在处理片段,没有适当的上下文
      • @xadm ...我们正在处理片段,没有适当的上下文。是的,你是对的
      【解决方案3】:

      我已尝试修复代码,并在 cmets 中详细解释了如何正确使用 useEffect 挂钩。我强烈建议您阅读 Dan Abramov 的 this blog post,它将消除您对使用 useEffect 钩子的大部分误解

      //since you data fetching code does not depend on anything inside your component you can hoist it outside the component.
      
      async function getAgentData(){
        try{
           const response = await fetch('http://localhost:3004/agentsdata');
           return await response.json();
        } catch(error){
          console.log(error)
        }
      }
      
      async function getQueueData{
        try{
           const response = await fetch('http://localhost:3004/queuedata');
           return await response.json();
        } catch(error){
          console.log(error)
        }
      }
      
      
      function YourComponent(props){
      
         // destructure your props
         const { location: { data }, match } = props;
         const { params: { user } } = props.match;
      
         // Split your hooks by concern keep the related code together
         useEffect(() => {
      
            setUser(user);
      
         // add ALL you dependencies for this effect
         }, [user])
      
         // Second Effect
         useEffect(() => {
      
           const pge = (data == undefined)? 1 : data.page;
           const lowerB = (0 +((pge - 1) * 10));
           const upperB = 10 + ((pge - 1) * 10);
      
           setPage(pge);
      
           async function fetchData(){
             const agentRes = await getAgentData();
             setAgents(agentRes);
      
             const queueRes = await getQueueData();
      
             // Note: There need to first save it to state; then retrieve then slice then update again. When all you need is this
             setBaseQueues(queueRes.slice(lowerB, upperB));
           }
      
           //call the function here
           fetchData();
      
           //Add the ALL the dependencies of this effect i.e all props, functions, state variables inside 
           //YourComponent that you have used in this effect
           //In this case this effect is only run when this.props.location.data is changes, which is the desired behaviour
         }, [ data ])
      
         return <>Whatever</>
      }
      
      

      【讨论】:

        猜你喜欢
        • 2020-10-20
        • 2020-06-07
        • 2021-10-02
        • 1970-01-01
        • 2020-04-14
        • 1970-01-01
        • 2022-01-22
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多