简体   繁体   English

在异步函数完成运行之前反应渲染

[英]React rendering before async function finishes running

The program below reads data from a Firebase db and then stores it via setState to a variable.下面的程序从 Firebase 数据库读取数据,然后通过 setState 将其存储到变量中。 On the first render, no {problem} is undefined and so it cannot be used in the code.在第一次渲染时,没有 {problem} 是未定义的,因此不能在代码中使用。 This means that React will just render a page with the hardcoded text.这意味着 React 只会渲染一个带有硬编码文本的页面。 As soon as setState is run, the components re-render and the program displays the data.一旦 setState 运行,组件就会重新渲染,程序会显示数据。 The only problem I have is that when I pass the {problem} variable to a child component, it returns the following error (error occurs on last line in AdditionalInfo.jsx shown).我唯一的问题是,当我将 {problem} 变量传递给子组件时,它返回以下错误(显示的 AdditionalInfo.jsx 中的最后一行出现错误)。

AdditionalInfo.jsx:45 Uncaught TypeError: Cannot read properties of undefined (reading 'map') AdditionalInfo.jsx:45 Uncaught TypeError: Cannot read properties of undefined (reading 'map')

Why does the program only return an error for the child component and how can I fix this?为什么程序只返回子组件的错​​误,我该如何解决这个问题?

Problem.jsx问题.jsx

const [problem, setProblem] = useState({});


    useEffect(() => {
        const getProblem = async () => {
            const problemsRef = collection(db, "problems");
            const q = query(problemsRef, where("problemNumber", "==", parseInt(params.id)));
            const querySnapshot = await getDocs(q);
            setProblem({...querySnapshot.docs[0].data()});
        }
        getProblem();
    }, [params.id]);
.
.
.

   return (
        <Split
        gutterSize={10}
        gutterAlign="center"
        cursor="col-resize"
        snapOffset={75}
        className='split'
        minSize={0}>
            <div className="bg-gray-100 min-h-screen max-h-screen overflow-y-auto"> {/* min-h-screen causes there to be a small scroll because of the navbar. To fix this use someting like min-h-screen minus height of navbar */}
                <div className='mx-2 my-2 text-1xl font-medium'>
                    {problem.problemNumber}. {problem.problemTitle}
                </div>
                <div className='mx-2 my-2 text-1xl font-normal text-green-600'>
                    {problem.problemType}
                </div>
                <Divider variant="middle" />
                <div className='mx-2 my-2 text-1xl text-base'>
                    {problem.problemQuestion}
                </div>

                <div>
                    {mapToArray(problem.companies).map(([key, value]) => {
                        return <CompanyTag key={key} companyName={value} companyId={key} />
                    }
                    )}
                </div>
                <AdditionalInfo problem={problem}/>
            </div>
                
            <div className="bg-gray-100">
                <div className='mx-7'>
                    <video width="100%" height="100%px" controls>
                        <source src="movie.mp4" type="video/mp4"></source>
                    </video>
                </div>
            </div>

        </Split>
    )   

AdditionalInfo.jsx附加信息.jsx

const AdditionalInfo = ({ problem }) => {

  return (
    <div>
        {problem.questionTips.map((tip) => {
            return <Tip key={tip.number} number={tip.number} description={tip.description} />
        })} // Code causes an error here
.
.
.

To answer as an answer.作为答案来回答。

Error is due you initially set const [problem, setProblem] = useState({});错误是由于您最初设置const [problem, setProblem] = useState({}); so on first render your problem is just an empty object.所以首先渲染你的问题只是一个空对象。 In the child component where you passed your problem you are trying to use problem.questionTips.map() fn in jsx and due to on first render problem.questionTips is undefined - trying to access .map on undefined gives you your error.在您传递problem的子组件中,您尝试在 jsx 中使用 problem.questionTips.map() fn 并且由于第一次渲染问题。questionTips 未定义 - 尝试在未定义时访问 .map 会给您错误。

You should use ?你应该使用? operator to solve this issue.运营商来解决这个问题。

{problem.questionTips?.map((tip) => {
  return <Tip key={tip.number} number={tip.number} description={tip.description} />
})}

Answering on additional question in the comment: "Is there a way to full wait until all the data is read from firebase before rendering?"回答评论中的其他问题:“有没有办法完全等到在渲染之前从 firebase 读取所有数据?”

Yes, there is a way, just use是的,有一种方法,只需使用

{problem?.questionTips && <AdditionalInfo problem={problem}/>}

That will not render your AdditionalInfo component until problem with questionTips is loaded在加载questionTips problem ,这不会呈现您的 AdditionalInfo 组件

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

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