javascript - 从组件中的 useState 多次调用状态更新程序会导致多次重新渲染
问题描述
我第一次尝试 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 };
};
在一个普通的类组件中,您将进行一次调用来更新可能是一个复杂对象的状态,但“挂钩方式”似乎是将状态拆分为更小的单元,其副作用似乎是多个重新当它们单独更新时呈现。任何想法如何减轻这种情况?
解决方案
您可以将loading
state 和data
state 组合成一个 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>
推荐阅读
- r - 在 ggplot 中更改地图的调色板
- typescript - 仅对对象的已定义成员强制类型,而不将“keysof typeof [object]”扩大到 string[]?
- amazon-web-services - 原始实例已终止,无法通过 EBS 快照恢复卷 RDP 进入新实例
- javascript - 如何显示实时倒计时然后关闭窗口?
- node.js - Firebase CLI Typescript 错误 TS2345 在之前编译的代码上引发
- reactjs - 我正在尝试安装 react-native-image-picker 但收到此错误
- c++ - make_heap 和 pop_heap 可以,但 push_heap 不行
- javascript - 如何获取不和谐频道的创建者?
- javascript - javascript if else 语句不起作用
- python - Django - 信号应该放在哪里?