当道具没有改变时,React备忘录会保持渲染

我有一个无状态的功能组件,它没有道具,并且从React上下文中填充内容。作为参考,我的应用程序使用NextJS,并且是一个同构应用程序。我试图在这个组件上第一次使用React.memo(),但是它始终在客户端页面更改时重新呈现,尽管道具和上下文没有更改。我知道这是由于我放置了控制台日志。

我组件的一个简单示例是:

const Footer = React.memo(() => {
  const globalSettings = useContext(GlobalSettingsContext);
  console.log('Should only see this once');

  return (
    <div>
      {globalSettings.footerTitle}
    </div>
  );
});

我什至尝试没有运气地传递第二个参数:

const Footer = React.memo(() => {
  ...
}, () => true);

任何想法出什么事了吗?

编辑:上下文提供程序的用法_app.js如下所示:

class MyApp extends App {
  static async getInitialProps({ Component, ctx }) {
    ...
    return { globalSettings };
  }

  render() {    
    return (
      <Container>
        <GlobalSettingsProvider settings={this.props.globalSettings}>
          ...
        </GlobalSettingsProvider>
      </Container>
    );
  }
}

实际的GlobalSettingsContext文件如下所示:

class GlobalSettingsProvider extends Component {
  constructor(props) {
    super(props);
    const { settings } = this.props;
    this.state = { value: settings };
  }

  render() {
    return (
      <Provider value={this.state.value}>
        {this.props.children}
      </Provider>
    );
  }
}

export default GlobalSettingsContext;
export { GlobalSettingsConsumer, GlobalSettingsProvider };
村村2020/03/23 15:53:13

问题出在哪里useContext每当您的上下文中的任何值更改时,无论您使用的值是否已更改,组件都将重新呈现。

解决方案是withMyContext()像这样创建一个HOC(即)。

// MyContext.jsx
// exported for when you really want to use useContext();
export const MyContext = React.createContext();

// Provides values to the consumer
export function MyContextProvider(props){
  const [state, setState] = React.useState();
  const [otherValue, setOtherValue] = React.useState();
  return <MyContext.Provider value={{state, setState, otherValue, setOtherValue}} {...props} />
}

// HOC that provides the value to the component passed.
export function withMyContext(Component){
  <MyContext.Consumer>{(value) => <Component {...value} />}</MyContext.Consumer>
}

// MyComponent.jsx
const MyComponent = ({state}) => {
  // do something with state
}

// compares stringified state to determine whether to render or not. This is
// specific to this component because we only care about when state changes, 
// not otherValue
const areEqual = ({state:prev}, {state:next}) => 
  JSON.stringify(prev) !== JSON.stringify(next)

// wraps the context and memo and will prevent unnecessary 
// re-renders when otherValue changes in MyContext.
export default React.memo(withMyContext(MyComponent), areEqual)

通过传递上下文作为道具,而不是在render中使用它,使我们能够隔离出我们实际上关心使用areEqual的变化值。在中渲染期间,无法进行此比较useContext

我会大力倡导将选择器作为第二个参数,类似于react-redux的新钩子useSelector。这将使我们能够做类似的事情

const state = useContext(MyContext, ({state}) => state);

谁的返回值只会在状态更改时更改,而不会在整个上下文更改时更改。

但是我只是一个梦想家。

这可能是我目前关于在简单应用程序的钩子上使用react-redux的最大争论。