当 props 没有改变时,React memo 会继续渲染
React memo keeps rendering when props have not changed
我有一个无状态功能组件,它没有道具并从 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 };
问题来自 useContext
。每当您的上下文中的任何值发生变化时,无论您使用的值是否发生变化,该组件都会 re-render。
解决方案是像这样创建一个 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)
将上下文作为 props 传递而不是在渲染中使用它允许我们使用 areEqual 隔离我们真正关心的变化值。在 useContext
.
内的渲染期间无法进行此比较
我强烈主张将选择器作为第二个参数,类似于 react-redux 的新钩子 useSelector。这将允许我们做类似
的事情
const state = useContext(MyContext, ({state}) => state);
谁的 return 值只会在状态改变时改变,而不是整个上下文。
但我只是一个梦想家
这可能是我现在对使用 react-redux over hooks 简单应用的最大争论。
我有一个无状态功能组件,它没有道具并从 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 };
问题来自 useContext
。每当您的上下文中的任何值发生变化时,无论您使用的值是否发生变化,该组件都会 re-render。
解决方案是像这样创建一个 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)
将上下文作为 props 传递而不是在渲染中使用它允许我们使用 areEqual 隔离我们真正关心的变化值。在 useContext
.
我强烈主张将选择器作为第二个参数,类似于 react-redux 的新钩子 useSelector。这将允许我们做类似
的事情const state = useContext(MyContext, ({state}) => state);
谁的 return 值只会在状态改变时改变,而不是整个上下文。
但我只是一个梦想家
这可能是我现在对使用 react-redux over hooks 简单应用的最大争论。