我有一个包含 2000 个元素的 React 组件,并根据一些过滤条件更新我的状态,这在内部会导致重新渲染。一切似乎都运行良好。但是当我从 2000 个元素切换到 1000 个元素并来回切换过滤器时,渲染需要很长时间,有时浏览器会冻结。我做了 chrome 时间线分析,主要耗时的部分是渲染。任何帮助,将不胜感激。
2 回答
正如@enjoylife 所建议的那样,这是一个很好的步骤,但是如果您的视图中有许多组件结构,那将很难调试,即使记住组件也无法消除连续或循环渲染。
我在遇到奇怪的冻结和奇怪的错误后了解到这一点,这些错误在用户登录主页时不会停止。想象一下所有屏幕。有时,您几乎不会注意到您的组件重新渲染。
使用控制台日志检测您的屏幕/页面(循环)重新渲染
const Home = () => {
conso.log('home re-rending')
// some hooks
return <BigComponent />
}
如上所写。日志显示的时间不得超过组件安装后的有限时间。就我而言,这是一次。但如果它太多(日志)并且肯定会冻结你的电脑。因此,请仔细遵循以下步骤并追溯您的步骤。
尝试此建议的解决方案之前的提示和先决条件。请确保您有样式指南设置,例如 Eslint,这很棒。就我而言,我用 复制了源代码cra
,然后整理出了我遇到的第一个和最后一个列出的问题。
小心使用 React 钩子,例如
useEffect
特别是。避免在组件中造成副作用。就我而言,我创建了一个可重用的useUpdateEffect hook
,我打算用它来解决这个名字是为了检测 React 道具或窗口道具的更新,但它适得其反,我不会分享代码。此外,如果您通过了正确和预期的依赖项,请额外检查一下,这个 Eslint 值得称赞。
避免在 React 列表中使用随机键。在组件列表中使用唯一且恒定的键,因为反应依赖于它来识别每个项目。根据反应库
键帮助 React 识别哪些项目已更改、添加或删除。应为数组内的元素提供键,以使元素具有稳定的身份。作为最后的手段,您可以使用项目索引作为键:
避免 reducer 和 React 组件中的变量名冲突。请考虑使用风格指南作为您的朋友,以避免今年秋天。
我犯了一个愚蠢的错误,创建了一个 Foo 类并在它的渲染函数中使用,这也导致了冻结的场景。在这里写给任何可能再次遇到这个问题的人。按照这个线程。
避免无限循环,想象一下一次渲染大量数据。这发生
以防万一你和我一样命运,我敦促你检查你的循环并确保你没有 += 而不是 -= (反之亦然)。那些无限循环可能会给脖子带来很大的痛苦。
保持你的 reducer 作为一个 reducer,避免 Action 创建者,一个在你的 reducer 中的 API 调用
using another reducer in your reducer
,例如,reducerA
在reducerB
. 当您调用 updatereducerA
inreducerB
时,更新 inreducerA
将触发更新,reducerB
从而导致页面/屏幕重新渲染多次。例如
// this react reducer in my case
// reducer js file - reducerB
const useBusinesses = () => {
// reducerB as discussed above - the loading context
const { loading } = useLoadingContext(); // the culprit
const [data, setData] = useState(initialState); // initial state,
const [state, dispatch] = useReducer(reducer, data);
useEffect(() => setData(state), [state, setData]);
const { businesses, errorMessage } = state;
const setBusinesses = (payload) => dispatch({ type: `${FETCH_BUSINESSES}_SUCCESS`, data: payload });
const setBusinessesError = (payload) => dispatch({ type: `${FETCH_BUSINESSES}_ERROR`, data: payload });
const fetchBusinesses = async (lglt, type = 'food', limit = 12) => {
try {
// update reducerB: triggers multiple update in reducerA while requesting is pending
loading(FETCH_BUSINESSES, true);
const request = await API.businesses.getWithquery(
`long=${lglt[0]}&latt=${lglt[1]}&limit=${limit}&type=${type}`
);
loading(FETCH_BUSINESSES, false);
setBusinesses(request.data);
} catch (err) {
loading(FETCH_BUSINESSES, false);
// if (!err.response) dispatch(alertMessage(FETCH_BUKKAS, true, 'Please check your network'));
setBusinessesError(err.response.data);
}
});
return { businesses, errorMessage, fetchBusinesses };
};
export const [BusinessesProvider, useBusinessesContext] = constate(useBusinesses);
//home js file
Home = () => {
const { fetchBusinesses } = useBusinessContext();
conso.log('home re-rending')
// some hooks
useEffect(() => {
console.log('am i in trouble, yes!, how many troubles')
fetchBusinesses(coordinates)
}, [fetchBusinesses, coordinates])
return <BigComponent />
}
一个快速的解决方法是实现shouldComponentUpdate
See the docs,无论哪个子组件被渲染约 2000 次。
shouldComponentUpdate: function(nextProps, nextState) {
return this.props.value !== nextProps.value;
}
另一个快速检查是问自己是否遵循使用小的、无国籍的孩子的惯例,只传递道具。如果没有,可能是时候重构了。