【问题标题】:{React Native} Firing a function once State is up-to-date{React Native} 状态更新后触发函数
【发布时间】:2020-08-19 16:36:22
【问题描述】:

我正在努力弄清楚如何在setArr(list) 被填充后触发我的moveToResults() 函数。我觉得应该有一个简单的方法来使用new Promise 来做到这一点,但我似乎无法弄清楚。

const [arr, setArr] = useState([]);

const handlePress =  ()=>{
    var list = [];
    firebase.firestore().collection('PostedFunEvents').where("location", "==" , place).get().then((querySnapshot) =>{
        querySnapshot.forEach((doc) =>{ 
                list.push(doc.data());                                                   
            })
             setArr(list);   
        }).then(()=>{
            console.log(arr.length);
        }).then(()=>{
            moveToResults(); // AT THIS POINT ARR.LENGTH IS STILL 0
        });
}

这是我的moveToResults() 函数:

    const moveToResults = () =>{ 
        navigation.navigate('Results', {arr: arr})
    }

问题

我如何确保moveToResults()fires 一次setArr(list) 完成?

我基本上想确保当moveToResults() 被触发时arr 被填充。否则我什么也不传递到下一个屏幕。

【问题讨论】:

    标签: reactjs react-native


    【解决方案1】:

    在实际发生重新渲染之前,不会反映状态更新。在类组件中,setState 提供了一个回调,当状态更新完成时会调用它,但是在钩子状态更新器中没有提供这样的回调

    处理此类场景的方法是利用 useEffect 并监听状态变化。但是,由于您不希望这是在 initialRender 上,因此您需要禁用 initialRender 上的效果

    const [arr, setArr] = useState([]);
    const initialRender = useRef(true);
    useEffect(() => {
        if(!initialRender.current) {
            moveToResults()
        } else {
          initialRender.current = false;
        }
    
    }, [arr]) // Listening on arr change
    
    
    const handlePress =  ()=>{
        var list = [];
        firebase.firestore().collection('PostedFunEvents').where("location", "==" , place).get().then((querySnapshot) =>{
            querySnapshot.forEach((doc) =>{ 
                    list.push(doc.data());                                                   
                })
                 setArr(list);   
            })
    }
    

    但是,上述解决方案仅适用于仅通过 handlePress 操作更新 arr 或者您想在数组发生更改时随时导航的情况。如果数组是从其他地方更新的,并且您不希望在每次数组更改时都执行导航,则不能使用上述方法

    处理此类场景的正确方法是不依赖状态更新,而是直接将参数传递给 moveToResults

    const [arr, setArr] = useState([]);
    
    const handlePress =  ()=>{
        firebase.firestore().collection('PostedFunEvents').where("location", "==" , place).get().then((querySnapshot) =>{
            var list = [];
            querySnapshot.forEach((doc) =>{ 
                    list.push(doc.data());                                                   
                })
                return list;   
            }).then((list)=>{
                moveToResults(list); 
            });
    }
    
    const moveToResults = (list) =>{ 
        navigation.navigate('Results', {arr: list})
    }
    

    通过编写函数moveToResults 来接受参数,您可以在任何地方使用它并依赖于传递更新的状态,而不是等待它使用来自状态的值。

    【讨论】:

    • 谢谢!我今天将对此进行测试并通知您。
    • 我在这里运行了一个console.log:.then((list)=>{ console.log(list) moveToResults(list); });列表未定义,因此 moveToResults 没有传递任何内容
    • 我对您提供的第一个解决方案更感兴趣,但问题是我需要软刷新才能触发 moveToResults() 函数。
    • 承诺链应该可以工作。然而,第一个解决方案也不需要软刷新。你能解释一下你的意思吗
    • 抱歉,您是否希望获得更多关于我所说的软刷新或其他内容的详细信息?
    【解决方案2】:

    使用querySnapshot.docs返回的数组设置状态:

    const [arr, setArr] = useState([]);
    
    // This callback will be called after 'arr' get elements.
    React.useEffect(() => {
      if (arr.length > 0) {
        moveToResults(arr); // go!
      }
    }, [arr]);
    
    const handlePress = () => {
      firebase.firestore().collection('PostedFunEvents').where("location", "==" , place).get()
      .then((querySnapshot) => {
        console.log(querySnapshot.docs); // debug array
    
        setArr(querySnapshot.docs); // setting list
    
        // moveToResults(querySnapshot.docs); // go!
      });
    }
    

    【讨论】:

    • 再次感谢您的帮助。当我尝试这个时,我遇到了一些问题。 var i 保持未定义且没有增量。当我创建一个 var i = 0 然后在每次迭代中递增它时, i 会增加,但 i === querySnapshot.length - 1 永远不会为真,因此该 if 语句中的代码不会执行。我认为这是因为 querySnapshot.length - 1 是 NaN。
    • 呃,我认为 querySnapshot 是一个对象而不是数组。让我做一些改变,等一下。
    • @IllllIl 我编辑了答案以添加一种设置数组的方法,从而绕过了对 foreach 的需求。你认为呢?这行得通吗?如果没有,请告诉 'querySnapshot.docs' 是否返回数组。
    • 非常感谢您抽出宝贵的时间。所以基本上正在发生的事情就是这样。 moveToResults() 被触发,但 arr 保持为空。 querySnapshot.docs 也返回数组
    • moveToResults 它没有触发。伙计,我投票支持你以获得所有帮助,我必须弄清楚一些事情......在这一点上不太确定,我为占用你这么多时间而感到难过。非常感谢
    【解决方案3】:

    为什么不使用 redux,在这种情况下会有很大帮助?
    不管怎样,我在想,是这样的:

    const [arr, setArr] = useState([]);
    
        const handlePress = () => {
           firebase.firestore()
                   .collection('PostedFunEvents')
                   .where("location", "==" , place)
                   .get()
                   .then(snapshot => {
    
            let list = [];
    
            if (snapshot.empty) {
              throw new Error('No location here.');
            }  
    
            snapshot.forEach(doc => {
              list.push(doc);
            });
    
            if (snapshot.length === list.length) {
              setArr(list)
            }
          })
          .catch(err => {
             console.log('Error getting documents', err);
          });
    }
    

    我希望它有所帮助,或者至少它有效

    【讨论】:

    • 感恩马可!意大利人 FTW。无论如何,我猜这里只有英语。非常感谢您对此进行尝试。我一定会对此进行测试,跟进您分享的内容。您建议我应该在哪里触发 moveToResults() ?
    • 我在哪里使用意大利语? :O
    • 无论如何,我在想,你可以用对不起,我完全忘记了 moveToResult 的部分,我会用一个 pastebin 再次评论以添加它
    • 不,你没有用意大利语发表评论,我做了。因为我注意到你也是意大利人,所以我正准备用意大利语发表评论,但后来我就像“不……这里只有英语”无论如何,谢谢,我会等待新的评论
    • 这里是:pastebin.com/PGV8fVb9,如果没有帮助我可以帮你告诉我
    【解决方案4】:

    您可以在 firebase 操作完成中使用异步等待,而不是使用多个 .then() 块,即

    firebase.firestore().collection('PostedFunEvents').where("location", "==" , place).get().then(async (querySnapshot) =>{
     await querySnapshot.forEach((doc) =>{ 
                list.push(doc.data());                                                   
            })
             setArr(list);   
        })
     .catch(err=>{
    console.log(err)
     })
    

    【讨论】:

    • On ` await querySnapshot.forEach((doc) =>` 我收到这条消息 await 对这个表达式的类型没有影响。
    • 就是这样:const [arr, setArr] = useState([]);我基本上只是在设置 arr 的状态
    【解决方案5】:

    arr 变量在你的组件重新渲染之前不会被填充,因此你不能在同一个承诺链中调用moveToResults。但是,这应该是在填充 arr 然后调用函数时进行的简单检查。

    const [arr, setArr] = useState([]);
    useEffect(() => {
      if (arr.length > 0) {
        moveToResults(arr);
      }
    }, [arr])
    
    const handlePress =  ()=>{
        var list = [];
        firebase.firestore().collection('PostedFunEvents').where("location", "==" , place).get().then((querySnapshot) =>{
                querySnapshot.forEach((doc) =>{ 
                    list.push(doc.data());                                                   
                })
                 // Use concat because it returns a new array which will allow react to detect a state change
                setArr(arr.concat(list));   
            });
    }
    

    【讨论】:

      【解决方案6】:

      我们可以编写自定义函数,如果状态发生任何变化,它将调用回调函数

      然后你可以从回调中触发函数

      import React, { useState, useEffect } from "react";
      import ReactDOM from "react-dom";
      
      import "./styles.css";
      
      const useStateCallbackWrapper = (initilValue, callBack) => {
        const [state, setState] = useState(initilValue);
        useEffect(() => callBack(state), [state]);
        return [state, setState];
      };
      
      const callBack = state => {
        console.log("---------------", state);
      };
      function App() {
        const [count, setCount] = useStateCallbackWrapper(0, callBack);
        return (
          <div className="App">
            <h1>{count}</h1>
            <button onClick={() => setCount(count + 1)}>+</button>
            <h2>Start editing to see some magic happen!</h2>
          </div>
        );
      }
      
      const rootElement = document.getElementById("root");
      ReactDOM.render(<App />, rootElement);
      

      【讨论】:

        【解决方案7】:

        试试这个家伙。 setArr 和 navigation 只能在列表可用后调用。

        const handlePress = async () => {
        
          const myPromise = new Promise((resolve, reject) => {
            const list = [];
            firebase
                .firestore()
                .collection('PostedFunEvents')
                .where("location", "==", place)
                .get()
                .then((querySnapshot) => {
                  querySnapshot.forEach(doc =>
                      list.push(doc.data()
                      ));
                  resolve(list);
                }).catch(reject);
          });
        
          try {
            const myList = await myPromise();
            console.log(myList);
            setArr(list);
            navigation.navigate('Results', {arr: myList});
          } catch (e) {
            console.log(e)
          }
        }
        

        【讨论】:

        • 谢谢,我会尽快测试并回复您
        猜你喜欢
        • 2018-10-01
        • 2020-09-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-04-04
        • 2020-04-24
        相关资源
        最近更新 更多