简体   繁体   English

在异步 function 中更新 state

[英]Updating state in async function

I'm very new to react so apologies if this question has already been answered or should be phrased differently.如果这个问题已经得到回答或应该以不同的方式表达,我很抱歉做出反应。 I have a functional component that fetches a.json file from my public folder in an async function (loadData()).我有一个功能组件,它从我的公共文件夹中的异步 function (loadData()) 中获取 a.json 文件。 Using the developer tools in my chrome window, I can see that the function gets me exactly what I want, yet the state doesn't seem to want to update when I use setData.使用我的 chrome window 中的开发人员工具,我可以看到 function 正是我想要的,但 state 在我使用 setData 时似乎不想更新。

Edit: So I think I know what the problem is, which is that the first time the component renders, the variable source needs that JSON object which won't be there until the component re-renders.编辑:所以我我知道问题出在哪里,即组件第一次渲染时,变量source需要 JSON object 在组件重新渲染之前不会存在。 If that's the case, should all the code starting at let source = pickRandomVerb(data) go somewhere outside useEffect()?如果是这种情况,所有从let source = pickRandomVerb(data) go 开始的代码都应该在 useEffect() 之外的某个地方吗?

function ComposedTextField() {

    const classes = useStyles();
    const [data, setData] = React.useState([]);
    const [displayVerb, setDisplayVerb] = React.useState('');

    const pickRandomVerb = (list) => {
        var obj_keys = Object.keys(list);
        var ran_key = obj_keys[Math.floor(Math.random() * obj_keys.length)];
        return list[ran_key];
    }

    const loadData = async() => {
        const response = await fetch('verbs.json');
        const json = await response.json();
        setData(json);
        console.log(json); //json is exactly what I want here
        console.log(data); //data is just '[]' here
    }

    useEffect(() => {

        loadData();
        console.log(data) //data is also '[]' here

        let source = pickRandomVerb(data)
        let verbSource = source.infinitive;
        let showVerb = verbSource.toString().replaceAll("\"", "");
        setDisplayVerb(showVerb)
    
    }, [])


    return(
        <div>
            <Typography className = {classes.form}>
                <p>{displayVerb}</p>
            </Typography>
        </div>
    )
}

Can anyone let me know what I'm missing?谁能让我知道我错过了什么? Thanks in advance.提前致谢。

setState is async also then you can't see the data changed in setState 也是异步的,那么您看不到数据已更改

    const loadData = async() => {
        const response = await fetch('verbs.json');
        const json = await response.json();
        setData(json);
        console.log(json); //json is exactly what I want here
        console.log(data); //data is just '[]' here
    }

Your useEffect doesn't define the dependency then it just run once.您的 useEffect 没有定义依赖项,然后它只运行一次。 If you want to see your data changed just add dependency for data to track when data changed.如果您想查看您的数据更改,只需添加数据依赖项以跟踪数据更改的时间。

   /// Just load at the first time you render you component
   useEffect(()=>{
       loadData();
    },[])

    useEffect(() => {

        console.log(data)

        let source = pickRandomVerb(data)
        let verbSource = source.infinitive;
        let showVerb = verbSource.toString().replaceAll("\"", "");
        setDisplayVerb(showVerb)
    
    }, [data]) //Just add dependency here for useEffect

useState hook is also asynchronous, and will not be reflected immediately and this question already addressed link-1 , link-2 useState 钩子也是异步的,不会立即反映,这个问题已经解决了link-1link-2

changes required (you have to listen the changes of the data in useEffect),需要更改(您必须在 useEffect 中监听数据的更改),

useEffect(() => {
   loadData();
    console.log(data) //data is also '[]' here
}, []);

useEffect(() => {
        let source = pickRandomVerb(data)
        let verbSource = source.infinitive;
        let showVerb = verbSource.toString().replaceAll("\"", "");
        setDisplayVerb(showVerb)
}, [data]);

Maybe you can try something like this也许你可以尝试这样的事情

function ComposedTextField() {

    const classes = useStyles();
    const [data, setData] = React.useState([]);
    const [displayVerb, setDisplayVerb] = React.useState('');

    const pickRandomVerb = (list) => {
        var obj_keys = Object.keys(list);
        var ran_key = obj_keys[Math.floor(Math.random() * obj_keys.length)];
        return list[ran_key];
    }

    const loadData = async() => {
        const response = await fetch('verbs.json');
        const json = await response.json();
        setData(json);
        console.log(json); //json is exactly what I want here
        console.log(data); //data is just '[]' here
    }

    useEffect(() => {

        loadData();
        console.log(data) //data is also '[]' here
    
    }, [])

    // Move that code outside useEffect and inside condition
    if(data!==[]){
        let source = pickRandomVerb(data)
        let verbSource = source.infinitive;
        let showVerb = verbSource.toString().replaceAll("\"", "");
        setDisplayVerb(showVerb)
    }

    return(
        <div>
            <Typography className = {classes.form}>
                <p>{displayVerb}</p>
            </Typography>
        </div>
    )
}

In this code setDisplayVerb() will be called when the data has some value.在此代码中,当数据具有某些值时,将调用 setDisplayVerb()。 The flow of the control would be something like this.控制流程是这样的。

  1. Component is mounted with data=[]使用 data=[] 安装组件
  2. usEffect is called which in turns calls loadData()调用 usEffect 依次调用 loadData()
  3. loadData will set the value of data using setData(json) loadData 将使用 setData(json) 设置数据的值
  4. When data is set it will cause re-render, since data has value this time, the if condition is satisfied and the statements inside it will be executed.设置数据时会导致重新渲染,由于本次数据有值,满足if条件,执行其中的语句。
  5. setDisplayVerb() will cause re-render of the application. setDisplayVerb() 将导致应用程序的重新渲染。
  6. Since the dependency array of useEffect is empty it will not be called again.由于 useEffect 的依赖数组为空,因此不会再次调用。

This way you should be able to see the data on your screen.这样,您应该能够在屏幕上看到数据。

so to expand on my comment, try this:所以要扩展我的评论,试试这个:

    const loadData = async() => {
        const response = await fetch('verbs.json');
        const json = await response.json();
        return json; /// <<< note: here you return data
    }

    useEffect(async () => {
        const data = await loadData(); // <<<< note: here you wait for data
        console.log(data);

        let source = pickRandomVerb(data)
        let verbSource = source.infinitive;
        let showVerb = verbSource.toString().replaceAll("\"", "");
        setDisplayVerb(showVerb)
    
    }, [])

this will only get you the data on initial load.这只会让您获得初始加载的数据。

Just as a side note: to improve a bit on this component, you can move your loadData function outside of the component.顺便说一句:要改进此组件,您可以将loadData function 移到组件之外。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM