5

我有一个包含 2000 个元素的 React 组件,并根据一些过滤条件更新我的状态,这在内部会导致重新渲染。一切似乎都运行良好。但是当我从 2000 个元素切换到 1000 个元素并来回切换过滤器时,渲染需要很长时间,有时浏览器会冻结。我做了 chrome 时间线分析,主要耗时的部分是渲染。任何帮助,将不胜感激。

在此处输入图像描述

4

2 回答 2

5

正如@enjoylife 所建议的那样,这是一个很好的步骤,但是如果您的视图中有许多组件结构,那将很难调试,即使记住组件也无法消除连续或循环渲染。

我在遇到奇怪的冻结和奇怪的错误后了解到这一点,这些错误在用户登录主页时不会停止。想象一下所有屏幕。有时,您几乎不会注意到您的组件重新渲染。

使用控制台日志检测您的屏幕/页面(循环)重新渲染

const Home = () => {
  conso.log('home re-rending')
  // some hooks

  return <BigComponent />
}

如上所写。日志显示的时间不得超过组件安装后的有限时间。就我而言,这是一次。但如果它太多(日志)并且肯定会冻结你的电脑。因此,请仔细遵循以下步骤并追溯您的步骤。

在此处输入图像描述

尝试此建议的解决方案之前的提示和先决条件。请确保您有样式指南设置,例如 Eslint,这很棒。就我而言,我用 复制了源代码cra,然后整理出了我遇到的第一个和最后一个列出的问题。

  1. 小心使用 React 钩子,例如useEffect特别是。避免在组件中造成副作用。就我而言,我创建了一个可重用的useUpdateEffect hook,我打算用它来解决这个名字是为了检测 React 道具或窗口道具的更新,但它适得其反,我不会分享代码。

    此外,如果您通过了正确和预期的依赖项,请额外检查一下,这个 Eslint 值得称赞。

  2. 避免在 React 列表中使用随机键。在组件列表中使用唯一且恒定的键,因为反应依赖于它来识别每个项目。根据反应库

    键帮助 React 识别哪些项目已更改、添加或删除。应为数组内的元素提供键,以使元素具有稳定的身份。作为最后的手段,您可以使用项目索引作为键:

  3. 避免 reducer 和 React 组件中的变量名冲突。请考虑使用风格指南作为您的朋友,以避免今年秋天。

    我犯了一个愚蠢的错误,创建了一个 Foo 类并在它的渲染函数中使用,这也导致了冻结的场景。在这里写给任何可能再次遇到这个问题的人。按照这个线程

  4. 避免无限循环,想象一下一次渲染大量数据。这发生

    以防万一你和我一样命运,我敦促你检查你的循环并确保你没有 += 而不是 -= (反之亦然)。那些无限循环可能会给脖子带来很大的痛苦。

  5. 保持你的 reducer 作为一个 reducer,避免 Action 创建者,一个在你的 reducer 中的 API 调用using another reducer in your reducer,例如,reducerAreducerB. 当您调用 update reducerAinreducerB时,更新 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 />
}
于 2020-04-28T23:31:59.220 回答
2

一个快速的解决方法是实现shouldComponentUpdate See the docs,无论哪个子组件被渲染约 2000 次。

shouldComponentUpdate: function(nextProps, nextState) {
  return this.props.value !== nextProps.value;
}

另一个快速检查是问自己是否遵循使用小的、无国籍的孩子的惯例,只传递道具。如果没有,可能是时候重构了。

于 2016-01-13T00:03:18.967 回答