小编典典

从组件中的 useState 多次调用状态更新程序会导致多次重新渲染

all

我第一次尝试 React
钩子,一切似乎都很好,直到我意识到当我获取数据并更新两个不同的状态变量(数据和加载标志)时,我的组件(数据表)被渲染了两次,即使两个调用状态更新程序发生在同一个函数中。这是我的
api 函数,它将两个变量都返回给我的组件。

const getData = url => {

    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);

    useEffect(async () => {

        const test = await api.get('/people')

        if(test.ok){
            setLoading(false);
            setData(test.data.results);
        }

    }, []);

    return { data, loading };
};

在一个普通的类组件中,您将进行一次调用来更新可能是一个复杂对象的状态,但“挂钩方式”似乎是将状态拆分为更小的单元,其副作用似乎是多个重新当它们单独更新时呈现。任何想法如何减轻这种情况?


阅读 212

收藏
2022-06-23

共1个答案

小编典典

您可以将loadingstate 和datastate 组合成一个 state
对象,然后您可以进行一次setState调用,并且只会进行一次渲染。

注意:setState类内组件不同,setState返回的
fromuseState不会将对象与现有状态合并,它会完全替换对象。如果要进行合并,则需要读取先前的状态并将其与新值合并。请参阅文档

在您确定存在性能问题之前,我不会过多担心过度调用渲染。渲染(在 React 上下文中)和将虚拟 DOM 更新提交到真实 DOM
是不同的事情。这里的渲染是指生成虚拟 DOM,而不是更新浏览器 DOM。React 可以批处理setState调用并使用最终的新状态更新浏览器 DOM。

const {useState, useEffect} = React;



function App() {

  const [userRequest, setUserRequest] = useState({

    loading: false,

    user: null,

  });



  useEffect(() => {

    // Note that this replaces the entire object and deletes user key!

    setUserRequest({ loading: true });

    fetch('https://randomuser.me/api/')

      .then(results => results.json())

      .then(data => {

        setUserRequest({

          loading: false,

          user: data.results[0],

        });

      });

  }, []);



  const { loading, user } = userRequest;



  return (

    <div>

      {loading && 'Loading...'}

      {user && user.name.first}

    </div>

  );

}



ReactDOM.render(<App />, document.querySelector('#app'));


<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>

<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

<div id="app"></div>

替代方案 - 编写您自己的状态合并钩子

const {useState, useEffect} = React;



function useMergeState(initialState) {

  const [state, setState] = useState(initialState);

  const setMergedState = newState =>

    setState(prevState => Object.assign({}, prevState, newState)

  );

  return [state, setMergedState];

}



function App() {

  const [userRequest, setUserRequest] = useMergeState({

    loading: false,

    user: null,

  });



  useEffect(() => {

    setUserRequest({ loading: true });

    fetch('https://randomuser.me/api/')

      .then(results => results.json())

      .then(data => {

        setUserRequest({

          loading: false,

          user: data.results[0],

        });

      });

  }, []);



  const { loading, user } = userRequest;



  return (

    <div>

      {loading && 'Loading...'}

      {user && user.name.first}

    </div>

  );

}



ReactDOM.render(<App />, document.querySelector('#app'));


<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>

<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

<div id="app"></div>
2022-06-23