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

标签 javascript reactjs react-hooks

我是第一次尝试 React hooks,一切看起来都很好,直到我意识到当我获取数据并更新两个不同的状态变量(数据和加载标志)时,我的组件(数据表)被渲染了两次,甚至尽管对状态更新器的两次调用都发生在同一个函数中。这是我的 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 };
};

在一个普通的类组件中,你会进行一次调用来更新状态,它可以是一个复杂的对象,但“ Hook 方式”似乎是将状态分成更小的单元,其副作用似乎是单独更新时多次重新渲染。有什么想法可以缓解这种情况吗?

最佳答案

您可以将 loading 状态和 data 状态合并到一个状态对象中,然后您可以执行一次 setState 调用,并且只会有一个渲染。

注意:与类组件中的setState不同,useState返回的setState不会合并对象在现有状态下,它完全替换了对象。如果你想进行合并,你需要自己读取以前的状态并将其与新值合并。引用docs .

在您确定存在性能问题之前,我不会太担心过度调用渲染。渲染(在 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>

备选方案 - 编写您自己的状态合并钩子(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>

关于javascript - 从组件中的 useState 多次调用状态更新程序会导致多次重新渲染,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53574614/

相关文章:

javascript - Uncaught ReferenceError : function is not defined jQuery

javascript - 在页面上拥有多个切换项目的最有效方法

reactjs - React-bootstrap 中的可滚动下拉列表

reactjs - 可以用 useMemo 代替 useEffect 来调用副作用函数

javascript - HTML 输入自动填充占位符

javascript - 从元素列表优化音频预加载

javascript - 三元运算符的附加条件

reactjs - 为什么我应该在这个例子中使用 Redux?

javascript - 没有第二个参数或一个等于 null 或 undefined 的 useEffect 会发生什么?

Javascript/React - 关于 .focus() 行为的问题