【问题标题】:React how to wait until state is updated without extra state variables?React 如何在没有额外状态变量的情况下等待状态更新?
【发布时间】:2021-01-11 02:55:29
【问题描述】:

我无法理解。简而言之,我有这两个变量在状态中使用

questionLoaded
gameOver

这个组件需要等待,直到它使用函数selectRandomQuestion完成获取数据。

因此,如果我删除questionLoaded,它会在循环地图时给我异常。

现在我必须创建另一个变量来检查游戏何时结束以取消渲染此组件,因此三元条件questionLoaded || gameOver ?

我尝试检查变量currentQuestion 以有条件地渲染,但由于某种原因,它会在循环遍历此属性的options 数组中的地图时出现异常。这意味着currentQuestion 在它自己的子属性options 在达到条件检查时有任何数据之前首先获取数据

问题是这看起来有点脏,我只用了几个星期的 react,我不确定这是否是处理状态更新的适当方法,或者是否有更好的方法来处理它.

总结一下,我的问题是在渲染组件的时候,当渲染依赖于一些条件时,我经常需要等待状态更新,而每次我需要一个新的条件,这意味着我需要另一组逻辑使用另一个状态变量。这意味着使用额外的逻辑创建另一个 useEffect,并在该变量更新时触发。

import React, { useState, useEffect } from 'react';
import './GuessPicture.css';
import questions from './data/questions.js';

function GuessPicture() {
    const [currentQuestion, setCurrentQuestion] = useState({});
    const [unansweredQuestions, setUnansweredQuestions] = useState([]);
    const [answeredQuestions, setAnsweredQuestions] = useState([]);
    const [questionLoaded, setQuestionLoaded] = useState(false);
    const [gameOver, setGameOver] = useState(false);

    useEffect(() => {
        setUnansweredQuestions(questions);
        selectRandomQuestion();
        setQuestionLoaded(true);
    }, []);

    useEffect(() => {
        if (unansweredQuestions.length > 0) nextQuestion();
        else alert('game over');
    }, [unansweredQuestions]);

    function selectRandomQuestion() {
        const index = Math.floor(Math.random() * questions.length);
        let selectedQuestion = questions[index];
        selectedQuestion.options = shuffle(selectedQuestion.options);
        setCurrentQuestion(selectedQuestion);
    }

    function nextQuestion() {
        const index = Math.floor(Math.random() * unansweredQuestions.length);
        let selectedQuestion = unansweredQuestions[index];
        selectedQuestion.options = shuffle(selectedQuestion.options);
        setCurrentQuestion(selectedQuestion);
    }

    // Fisher Yates Shuffle Algorithm
    function shuffle(array) {
        var currentIndex = array.length,
            temporaryValue,
            randomIndex;

        // While there remain elements to shuffle...
        while (0 !== currentIndex) {
            // Pick a remaining element...
            randomIndex = Math.floor(Math.random() * currentIndex);
            currentIndex -= 1;

            // And swap it with the current element.
            temporaryValue = array[currentIndex];
            array[currentIndex] = array[randomIndex];
            array[randomIndex] = temporaryValue;
        }

        return array;
    }

    const onClickOption = (event) => {
        if (currentQuestion.correctValue == event.target.dataset.value) {
            setAnsweredQuestions((answeredQuestions) => [
                ...answeredQuestions,
                currentQuestion,
            ]);

            const newUnansweredQuestions = unansweredQuestions.filter(
                (item) => item.id != currentQuestion.id
            );

            setUnansweredQuestions(newUnansweredQuestions);
        } else alert('Wrong');
    };

    return (
        <>
            {questionLoaded || gameOver ? (
                <div className="guess-picture-container">
                    <div className="guess-picture">
                        <img src={currentQuestion.image} alt="English 4 Fun" />
                    </div>
                    <div className="guess-picture-answers-grid">
                        {currentQuestion.options.map((key) => (
                            <button
                                key={key.value}
                                onClick={onClickOption}
                                data-value={key.value}
                            >
                                {key.display}
                            </button>
                        ))}
                    </div>
                </div>
            ) : null}
        </>
    );
}

export default GuessPicture;

【问题讨论】:

  • Hooks 大多只是简单的 js 代码。无论复杂的逻辑如何发展,总有办法清理代码。干,在适当的水平抽象。你知道这些规则。钩子的一个好处是它们只是函数™️。所以解耦更容易。将它们拉入其他文件,将它们导入回组件内部使用,只要适合您。我不认为保持东西干净是钩子的问题。
  • 我明白了,我认为如果在更新状态时有一个回调并且可以在它之后运行登录,一切都会更容易
  • 嗯,组件函数体本身保证在每次更新后运行……所以不太确定你想要什么。还有更具体的吗?
  • 顺便说一句,我认为与 Glen 您当前的代码 LGTM 相似。除了我不会把所有这些函数都塞进组件函数之外,看起来很拥挤。但是代码本身很好。
  • @hackape 我明白了,所以任何额外的功能和逻辑都应该放在单独的文件夹中

标签: reactjs


【解决方案1】:

您的代码对我来说看起来不错,但是在这种情况下我会避免使用三元运算符,而是使用短路运算符。这使您的代码更简洁。

代替:

{questionLoaded || gameOver ? &lt;SomeComponentHere /&gt; : null}

尝试使用:

{(questionLoaded || gameOver) &amp;&amp; &lt;SomeComponentHere /&gt;}

为什么要避免使用三元运算符?因为它不是三元运算,所以它是一个布尔检查。这使您的代码在语义上更合适。

【讨论】:

  • 所以基本上我有更多的逻辑没有办法让它更干净?因为在某些时候似乎势不可挡,所以引入了更多的逻辑并且需要跟踪所有的钩子
  • 我还会将 Fisher-Yates 算法移动到另一个文件,可能在 utils 文件夹中。
  • 我的首选风格是在返回组件之前急切返回,例如:if (!questionLoaded || !gameOver) return null;
  • 我确实更喜欢 if 守卫,而不是喜欢有很多括号。
猜你喜欢
  • 1970-01-01
  • 2020-12-28
  • 2019-09-02
  • 2023-03-20
  • 1970-01-01
  • 2019-03-16
  • 2017-06-08
  • 1970-01-01
  • 2022-07-15
相关资源
最近更新 更多