简体   繁体   English

React useState 被覆盖,即使有传播

[英]React useState overwritten even with spread

So I have a component where I have to make an API call to get some data that has IDs that I use for another async API call.所以我有一个组件,我必须在其中进行 API 调用以获取一些具有我用于另一个异步 API 调用的 ID 的数据。 My issue is I can't get the async API call to work correctly with updating the state via spread (...) so that the checks in the render can be made for displaying specific stages related to specific content.我的问题是我无法让异步 API 调用正常工作,通过传播 (...) 更新 state 以便可以在渲染中进行检查以显示与特定内容相关的特定阶段。

FYI: Project is a Headless Drupal/React.仅供参考:项目是一个无头 Drupal/React。

import WidgetButtonMenu from '../WidgetButtonMenu.jsx';
import { WidgetButtonType } from '../../Types/WidgetButtons.tsx';
import { getAllInitaitives, getInitiativeTaxonomyTerm } from '../../API/Initiatives.jsx';
import { useEffect } from 'react';
import { useState } from 'react';

import { stripHTML } from '../../Utilities/CommonCalls.jsx';

import '../../../CSS/Widgets/WidgetInitiativeOverview.css';

import iconAdd from '../../../Icons/Interaction/icon-add.svg';

function WidgetInitiativeOverview(props) {
    const [initiatives, setInitiatives] = useState([]);
    const [initiativesStages, setInitiativesStage] = useState([]);

    // Get all initiatives and data
    useEffect(() => {
        const stages = [];
        const asyncFn = async (initData) => {
            await Promise.all(initData.map((initiative, index) => {
                getInitiativeTaxonomyTerm(initiative.field_initiative_stage[0].target_id).then((data) => {
                    stages.push({
                        initiativeID: initiative.nid[0].value,
                        stageName: data.name[0].value
                    });
                });
            }));

            return stages;
        }

        // Call data
        getAllInitaitives().then((data) => {
            setInitiatives(data);
            asyncFn(data).then((returnStages) => {
                setInitiativesStage(returnStages);
            })
        });
    }, []);

    useEffect(() => {
        console.log('State of stages: ', initiativesStages);
    }, [initiativesStages]);

    return (
        <>
            <div className='widget-initiative-overview-container'>
                <WidgetButtonMenu type={ WidgetButtonType.DotsMenu } />
                { initiatives.map((initiative, index) => {
                    return (
                        <div className='initiative-container' key={ index }>
                            <div className='top-bar'>
                                <div className='initiative-stage'>
                                    { initiativesStages.map((stage, stageIndex) => {
                                        if (stage.initiativeID === initiative.nid[0].value) {
                                            return stage.stageName;
                                        }
                                    }) }
                                </div>
                                <button className='btn-add-contributors'><img src={ iconAdd } alt='Add icon.' /></button>
                            </div>
                            <div className='initiative-title'>{ initiative.title[0].value } - NID ({ initiative.nid[0].value })</div>
                            <div className='initiative-description'>{ stripHTML(initiative.field_initiative_description[0].processed) }</div>
                        </div>
                    );
                }) }
            </div>
        </>
    );
}

export default WidgetInitiativeOverview;

Here's a link for video visualization: https://vimeo.com/743753924 .这是视频可视化的链接: https://vimeo.com/743753924 In the video you can see that on page refresh, there is not data within the state but if I modify the code (like putting in a space) and saving it, data populates for half a second and updates correctly within the component.在视频中,您可以看到在页面刷新时,state 中没有数据,但如果我修改代码(例如放入空格)并保存它,数据会填充半秒并在组件中正确更新。

I've tried using spread to make sure that the state isn't mutated but I'm still learning the ins and outs of React.我尝试使用 spread 来确保 state 没有发生突变,但我仍在学习 React 的细节。

The initiatives state works fine but then again that's just 1 API call and then setting the data. state 倡议工作正常,但话又说回来,这只是 1 个 API 调用然后设置数据。 The initiativeStages state can use X amount of API calls depending on the amount of initiatives are returned during the first API call. InitiativeStages state 可以使用 X 数量的 API 调用,具体取决于在第一个 API 调用期间返回的倡议数量。

I don't think the API calls are necessary for this question but I can give reference to them if needed.我不认为 API 调用对于这个问题是必要的,但如果需要,我可以参考它们。 Again, I think it's just the issue with updating the state.同样,我认为这只是更新 state 的问题。

the function you pass to initData.map() does not return anything, so your await Promise.all() is waiting for an array of Promise.resolve(undefined) to resolve, which happens basically instantly, certainly long before your requests have finished and you had a chance to call stages.push({... });您传递给initData.map()的 function 不会返回任何内容,因此您的await Promise.all()正在等待一系列Promise.resolve(undefined) .你有机会调用stages.push({... });

That's why you setInitiativesStage([]) an empty array.这就是为什么你setInitiativesStage([])一个空数组。

And what you do with const stages = [];你用const stages = [];做什么and the stages.push() inside of the .then() is an antipattern, because it produces broken code like yours.并且stages.push()内部的 stage.push( .then()是一种反模式,因为它会产生像您这样的损坏代码。

that's how I'd write that effect:我就是这样写效果的:

useEffect(() => {
  // makes the request for a single initiative and transforms the result.
  const getInitiative = initiative => getInitiativeTaxonomyTerm(
      initiative.field_initiative_stage[0].target_id
    ).then(data => ({
      initiativeID: initiative.nid[0].value,
      stageName: data.name[0].value
    }))

  // Call data
  getAllInitaitives()
    .then((initiatives) => {
      setInitiatives(initiatives);

      Promise.all(initiatives.map(getInitiative))
        .then(setInitiativesStage);
    });
}, []);

this code still has a flaw (imo.) it first updates setInitiatives , then starts to make the API calls for the initiaives themselves, before also updating setInitiativesStage .这段代码仍然有一个缺陷(imo。)它首先更新setInitiatives ,然后开始让 API 自己调用 initiaives ,然后再更新setInitiativesStage So there is a (short) period of time when these two states are out of sync.因此,这两种状态在一段时间内不同步。 You might want to delay setInitiatives(initiatives);您可能想延迟setInitiatives(initiatives); until the other API requests have finished.直到其他 API 请求完成。

getAllInitaitives()
  .then(async (initiatives) => {
    const initiativesStages = await Promise.all(initiatives.map(getInitiative));
    
    setInitiatives(initiatives);
    setInitiativesStage(initiativesStages)
  });

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

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