简体   繁体   English

道具未更改时,React备忘录会保持渲染

[英]React memo keeps rendering when props have not changed

I have a stateless functional component which has no props and populates content from React context. 我有一个无状态的功能组件,它没有道具,并且从React上下文中填充内容。 For reference, my app uses NextJS and is an Isomorphic App. 作为参考,我的应用程序使用NextJS,并且是一个同构应用程序。 I'm trying to use React.memo() for the first time on this component but it keeps re-rendering on client side page change, despite the props and context not changing. 我试图在这个组件上第一次使用React.memo(),但是它仍然在客户端页面更改时重新呈现,尽管道具和上下文没有更改。 I know this due to my placement of a console log. 我知道这是由于我放置了控制台日志。

A brief example of my component is: 我组件的一个简单示例是:

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

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

I've even tried passing the second parameter with no luck: 我什至尝试没有运气传递第二个参数:

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

Any ideas what's going wrong here? 任何想法出什么事了吗?

EDIT: Usage of the context provider in _app.js looks like this: 编辑: _app.js中的上下文提供程序的_app.js如下所示:

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

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

The actual GlobalSettingsContext file looks like this: 实际的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 };

The problem is coming from useContext . 问题来自useContext Whenever any value changes in your context, the component will re-render regardless of whether the value you're using has changed. 每当您的上下文中的任何值更改时,无论您使用的值是否已更改,组件都将重新呈现。

The solution is to create a HOC (ie withMyContext() ) like so; 解决方案是像这样创建一个HOC(即withMyContext() )。

// 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)

Passing context as props instead of using it within render allows us to isolate the changing values we actually care about using areEqual. 通过传递上下文作为道具,而不是在render中使用它,使我们能够隔离出我们真正关心的使用areEqual的变化值。 There's no way to make this comparison during render within useContext . useContext中的渲染期间,无法进行此比较。

I would be a huge advocate for having a selector as a second argument similar to react-redux's new hooks useSelector. 我会大力倡导将选择器作为第二个参数,类似于react-redux的新钩子useSelector。 This would allow us to do something like 这将使我们能够做类似的事情

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

Who's return value would only change when state changes, not the entire context. 谁的返回值只会在状态更改时更改,而不是在整个上下文更改时更改。

But I'm just a dreamer. 但是我只是一个梦想家。

This is probably the biggest argument I have right now for using react-redux over hooks for simple apps. 这可能是我目前关于在简单应用程序的钩子上使用react-redux的最大争论。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM