提问者:小点点

为功能组件更新等效,以忽略状态更改


我的代码有一个组件,它既有道具,又有自己的内部状态
组件只有在其道具发生变化时才应重新加载。状态更改不应触发重新加载
此行为可以通过基于类的组件和自定义的shouldComponentUpdate函数实现
然而,这将是代码库中第一个基于类的组件。一切都是通过功能组件和挂钩完成的。因此,我想知道是否可以使用功能组件对所需的功能进行编码。

在回答了一些与实际问题不符的问题后,我想我必须重新表述我的问题。以下是一个包含两个组件的最小示例:

  • 内线有一个道具和状态。这是正在讨论的组件。状态更改后,它不能重新启动。道具更改应触发重新渲染

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

我nner.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>
        )
    }
}

outer.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>
    </>
  );
}

官方文档说用React.memo包装组件。但这似乎不适用于防止状态变化的重新呈现。它只适用于道具更改。

我试着让做出反应。备忘录工作。您可以在这里看到代码的一个版本,其中外部和内部都是功能组件。

相关问题:

如何在React Hooks中使用应该组件更新?:这个问题只涉及道具更改。接受的答案建议使用React.memo

应该在功能组件中更新组件:这个问题早于有状态的功能组件。公认的答案解释了为什么功能组件不需要shouldComponentUpdate,因为它们是无状态的。


共3个答案

匿名用户

不停止状态更改

反应备忘录只检查道具的变化。如果您的功能组件包装在React中。memo的实现中有一个useState或useContext钩子,当状态或上下文发生变化时,它仍然会重新启动。

参考:-https://reactjs.org/docs/react-api.html#reactmemo

匿名用户

您的内部组件取决于外部组件的属性num,您不能阻止它呈现属性更改,因为React.memo进行属性比较:

// The default behaviour is shallow comparison between previous and current render properties.
const areEqual = (a, b) => a.num === b.num;
export default React.memo(Inner, areEqual);

通过记忆内部组件并删除num依赖项,它将不会在外部渲染时渲染,请参见附带的沙盒。

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

  return (
    <>
      ...
    // v Inner is memoized and won't render on `outerState` change.
      <Inner />
    </>
  );
}

如果你想用钩子实现\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

const [currState] = useState();
// shouldUpdateState your's custom function to compare and decide if update state needed
setState(prevState => {
  if(shouldUpdateState(prevState,currState)) {
    return currState;
  }
  return prevState;
});

匿名用户

React是由setState驱动的设计-

您可以拉入一个const state=useRef({})。当前以存储内部状态。

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

尽管如此,仍然有一个合理的问题要问:“如何以反应挂钩的方式实现应该组件更新?”解决办法是:

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])
}

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