从组件中的 useState 多次调用状态更新程序会导致多次重新渲染 [英] Multiple calls to state updater from useState in component causes multiple re-renders

查看:31
本文介绍了从组件中的 useState 多次调用状态更新程序会导致多次重新渲染的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

const getData = url =>{const [data, setData] = useState(null);const [loading, setLoading] = useState(true);useEffect(async() => {const test = await api.get('/people')如果(测试.确定){设置加载(假);setData(test.data.results);}}, []);返回{数据,加载};};

在普通的类组件中,您只需调用一次即可更新可能是复杂对象的状态,但钩子方式"似乎是将状态拆分为更小的单元,其副作用似乎是单独更新时多次重新渲染.任何想法如何减轻这种情况?

解决方案

你可以将 loading 状态和 data 状态组合成一个状态对象,然后你可以做一个setState 调用,只会有一个渲染.

注意: 与类组件中的 setState 不同,从 useState 返回的 setState 不合并对象使用现有状态,它完全替换对象.如果要进行合并,则需要阅读以前的状态并将其与新值合并.请参阅文档.

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

const {useState, useEffect} = React;功能应用(){const [userRequest, setUserRequest] = useState({加载:假,用户:空,});useEffect(() => {//请注意,这会替换整个对象并删除用户密钥!setUserRequest({loading: true });fetch('https://randomuser.me/api/').then(results => results.json()).then(数据=> {设置用户请求({加载:假,用户:data.results[0],});});}, []);const { 加载,用户 } = 用户请求;返回 (<div>{加载&&'加载中...'}{用户&&用户名.first}

);}ReactDOM.render(, 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;函数 useMergeState(initialState) {const [state, setState] = useState(initialState);const setMergedState = newState =>setState(prevState => Object.assign({}, prevState, newState));返回 [状态,setMergedState];}功能应用(){const [userRequest, setUserRequest] = useMergeState({加载:假,用户:空,});useEffect(() => {setUserRequest({loading: true });fetch('https://randomuser.me/api/').then(results => results.json()).then(数据=> {设置用户请求({加载:假,用户:data.results[0],});});}, []);const { 加载,用户 } = 用户请求;返回 (<div>{加载&&'加载中...'}{用户&&用户名.first}

);}ReactDOM.render(, 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>

I'm trying React hooks for the first time and all seemed good until I realised that when I get data and update two different state variables (data and loading flag), my component (a data table) is rendered twice, even though both calls to the state updater are happening in the same function. Here is my api function which is returning both variables to my component.

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 };
};

In a normal class component you'd make a single call to update the state which can be a complex object but the "hooks way" seems to be to split the state into smaller units, a side effect of which seems to be multiple re-renders when they are updated separately. Any ideas how to mitigate this?

解决方案

You could combine the loading state and data state into one state object and then you could do one setState call and there will only be one render.

Note: Unlike the setState in class components, the setState returned from useState doesn't merge objects with existing state, it replaces the object entirely. If you want to do a merge, you would need to read the previous state and merge it with the new values yourself. Refer to the docs.

I wouldn't worry too much about calling renders excessively until you have determined you have a performance problem. Rendering (in the React context) and committing the virtual DOM updates to the real DOM are different matters. The rendering here is referring to generating virtual DOMs, and not about updating the browser DOM. React may batch the setState calls and update the browser DOM with the final new state.

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>

Alternative - write your own state merger hook

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>

这篇关于从组件中的 useState 多次调用状态更新程序会导致多次重新渲染的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
前端开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆