更新两个状态变量时,组件两次呈现两次是否正常?

时间:2020-06-22 11:49:44

标签: reactjs

在我的测试应用程序中,每次调用某个函数时,都会调用我的API(axios.get),然后使用从数据库接收的数据更新两个状态变量。这两个状态变量都改变了屏幕上显示的内容。

问题是,我添加了一个useEffect钩子来“调试”重新渲染的数量,并且我注意到该组件被重新渲染两次,我想是因为它一次用于一个状态变量,而一次用于另一状态变量。我以为使用useReducer会改变这一点,但不会改变。

这是正常的React行为还是我应该做一些不同的事情才能使组件仅重新渲染一次?

编辑:我正在编辑以添加代码: (这是一种琐事测试应用程序,我是React的新手,所以我正在练习)

import React, { useEffect, useReducer } from 'react'
import axios from 'axios'
import './App.css';
import reducer from './Reducer.js'

const initialState = {
    question: '',
    id: 1,
    choices: []
}

const Questions = () => {

    const [state, dispatch] = useReducer(reducer, initialState)

    useEffect(() => {
        console.log('executed');
    })


    const getQuestion = async (e) => {

        try {
            e.preventDefault()
            
            const res = await axios.get(`/questions/${state.id}`)
            
            dispatch({
                type: 'set_question',
                payload:
                    res.data.question
            })
            dispatch({
                type: 'set_choices',
                payload:
                    [res.data.correct_answer,
                    res.data.incorrect_answer1,
                    res.data.incorrect_answer2]
            })

        } catch (err) {
            console.log(err);
        }
    }


    return (
        <div>

            <form onSubmit={getQuestion}>
                <button>Get next question</button>
            </form>

            <h1> {state.question ? state.question : null}</h1>
            <button> {state.choices ? `${state.choices[0]}` : null}</button>
            <button> {state.choices ? ` ${state.choices[1]}` : null}</button>
            <button> {state.choices ? ` ${state.choices[2]}` : null}</button>
        </div>
    )
}

export default Questions

减速器:

const reducer = (state, action) => {

    switch (action.type) {

        case 'set_question':
            return {
                ...state,
                question: action.payload
            }

        case 'set_choices':
            return {
                ...state,
                choices: action.payload
            }

        default:
            return state

    }
}

export default reducer

1 个答案:

答案 0 :(得分:1)

仅对事件处理程序和生命周期方法中的状态更新进行批处理。如果状态更新发生在异步函数中,例如作为对成功调用fetch或setTimeout的响应,它们将不会被批处理。宣布将在React的未来版本中进行更改。

另请参阅Dan Abramov对此的回答: https://stackoverflow.com/a/48610973/5005177

但是,在React 16和早期版本中,默认情况下,在React事件处理程序之外都没有批处理。因此,如果在您的示例中使用的是AJAX响应处理程序而不是handleClick,则每个setState()都会在发生时立即得到处理。在这种情况下,是的,您会看到一个中间状态。

promise.then(() => {
    // We're not in an event handler, so these are flushed separately.
    this.setState({a: true}); // Re-renders with {a: true, b: false }
    this.setState({b: true}); // Re-renders with {a: true, b: true }
    this.props.setParentState(); // Re-renders the parent
});

如果只希望一次重新渲染组件,则必须将所有数据保留在一个状态对象中,这样就只需调用setState一次(或者如果需要,则调用dispatch)对新数据使用useReducer

另一种解决方法是将状态更新块包装在ReactDOM.unstable_batchedUpdates(() => {...})中,这在以后的版本中很可能不再需要了。另请从上方查看Dan Abramov的答案以了解详细信息。

相关问题