shouldComponentUpdate 相当于功能组件,忽略状态更改

lhk*_*lhk 5 javascript reactive-programming reactjs react-hooks

我的代码有一个组件,它接受两个 props 并拥有自己的内部状态。
仅当组件的 props 更改时,组件才应重新渲染。状态更改不应触发重新渲染。
此行为可以使用基于类的组件和自定义shouldComponentUpdate函数来实现。
然而,这将是代码库中第一个基于类的组件。一切都是通过功能组件和钩子完成的。因此我想知道是否可以使用功能组件来编写所需的功能。

在一些没有解决实际问题的答案之后,我想我必须重新表述我的问题。这是一个包含两个组件的最小示例:

  • Inner 需要一个 prop 并具有状态。这是有问题的组件。状态更改后不得重新渲染。道具更改应该触发重新渲染。
  • 外层是内层的包裹物。它在这个问题的范围内没有任何意义,只是为 Inner 提供道具并模拟道具变化。

为了演示所需的功能,我使用基于类的组件实现了 Inner。该代码的实时版本可以在codesandbox 上找到。如何将其迁移到功能组件:

Inner.tsx:

import React, { Component } from 'react'

interface InnerProps{outerNum:number}
interface InnerState{innerNum:number}

export default class Inner extends Component<InnerProps, InnerState> {
    state = {innerNum:0};

    shouldComponentUpdate(nextProps:InnerProps, nextState:InnerState){
        return this.props != nextProps;
    }
    render() {
        return (
            <button onClick={()=>{
                this.setState({innerNum: Math.floor(Math.random()*10)})
            }}>
                {`${this.props.outerNum}, ${this.state.innerNum}`}
            </button>
        )
    }
}
Run Code Online (Sandbox Code Playgroud)

外部.tsx:

import React, { useState } from "react";
import Inner from "./Inner";

export default function Outer() {
  const [outerState, setOuterState] = useState(1);

  return (
    <>
      <button
        onClick={() => {
          setOuterState(Math.floor(Math.random() * 10));
        }}
      >
        change outer state
      </button>
      <Inner outerNum={outerState}></Inner>
    </>
  );
}
Run Code Online (Sandbox Code Playgroud)

官方文档说将组件包装在React.memo. 但这似乎并不能阻止状态更改时的重新渲染。它仅适用于道具更改。

我已经尝试过React.memo工作了。您可以在此处查看外部和内部都是功能组件的代码版本。

相关问题:

如何将 shouldComponentUpdate 与 React Hooks 一起使用?:这个问题只涉及道具的变化。接受的答案建议使用React.memo

函数组件中的 shouldComponentUpdate:这个问题早于有状态函数组件。接受的答案解释了功能组件如何不需要,shouldComponentUpdate因为它们是无状态的。

hac*_*ape 0

React 的设计是由 setState -> 重新渲染循环驱动的。Props 更改实际上是父组件中某处的 setState。如果您不希望 setState 触发重新渲染,那么为什么首先要使用它呢?

您可以拉入 aconst state = useRef({}).current来存储您的内部状态。

function InnerFunc(props) {
  const state = useRef({ innerNum: 0 }).current;
  return (
    <button
      onClick={() => {
        state.innerNum = Math.floor(Math.random() * 10);
      }}
    >
      {`${props.outerNum}, ${state.innerNum}`}
    </button>
  );
}
Run Code Online (Sandbox Code Playgroud)

也就是说,这仍然是一个值得问的问题:“如何以 React hook 方式实现 shouldComponentUpdate?” 这是解决方案:

function shouldComponentUpdate(elements, predicate, deps) {
  const store = useRef({ deps: [], elements }).current
  const shouldUpdate = predicate(store.deps)
  if (shouldUpdate) {
    store.elements = elements
  }
  store.deps = deps
  return store.elements
}

// Usage:

function InnerFunc(props) {
  const [state, setState] = useState({ innerNum: 0 })
  const elements = (
    <button
      onClick={() => {
        setState({ innerNum: Math.floor(Math.random() * 10) });
      }}
    >
      {`${props.outerNum}, ${state.innerNum}`}
    </button>
  );

  return shouldComponentUpdate(elements, (prevDeps) => {
    return prevDeps[0] !== props
  }, [props, state])
}
Run Code Online (Sandbox Code Playgroud)

请注意,调用时不可能阻止重新渲染周期setState,上面的钩子仅确保重新渲染的结果与先前渲染的结果保持相同。