gpt4 book ai didi

javascript - React Context API 和避免重新渲染

转载 作者:数据小太阳 更新时间:2023-10-29 03:56:58 25 4
gpt4 key购买 nike

我已经在底部更新了这个

有没有一种方法可以通过多个 Context API 消费者处理他们自己的提供者值部分来维护一个单一的根状态(如 Redux),而不会在每个孤立的更改上触发重新渲染?

已经read through this related question并尝试了一些变体来测试那里提供的一些见解,但我仍然对如何避免重新渲染感到困惑。

完整代码如下,在线:https://codesandbox.io/s/504qzw02nl

问题是,根据 devtools,每个组件都会看到“更新”(重新渲染),即使 SectionB 是唯一看到任何渲染更改的组件,即使 b 是状态树中唯一发生变化的部分。我已经尝试使用功能组件和 PureComponent 进行此操作,并看到相同的渲染抖动。

因为没有任何东西作为 Prop 传递(在组件级别),所以我看不出如何检测或防止这种情况。在这种情况下,我将整个应用程序状态传递给提供者,但我也尝试传递状态树的片段并看到相同的问题。显然,我做错了什么。

import React, { Component, createContext } from 'react';

const defaultState = {
a: { x: 1, y: 2, z: 3 },
b: { x: 4, y: 5, z: 6 },
incrementBX: () => { }
};

let Context = createContext(defaultState);

class App extends Component {
constructor(...args) {
super(...args);

this.state = {
...defaultState,
incrementBX: this.incrementBX.bind(this)
}
}

incrementBX() {
let { b } = this.state;
let newB = { ...b, x: b.x + 1 };
this.setState({ b: newB });
}

render() {
return (
<Context.Provider value={this.state}>
<SectionA />
<SectionB />
<SectionC />
</Context.Provider>
);
}
}

export default App;

class SectionA extends Component {
render() {
return (<Context.Consumer>{
({ a }) => <div>{a.x}</div>
}</Context.Consumer>);
}
}

class SectionB extends Component {
render() {
return (<Context.Consumer>{
({ b }) => <div>{b.x}</div>
}</Context.Consumer>);
}
}

class SectionC extends Component {
render() {
return (<Context.Consumer>{
({ incrementBX }) => <button onClick={incrementBX}>Increment a x</button>
}</Context.Consumer>);
}
}

编辑:我知道可能有 a bug in the way react-devtools检测或显示重新渲染。我有 expanded on my code above以显示问题的方式。我现在无法判断我正在做的事情是否真的导致了重新渲染。根据我从 Dan Abramov 那里读到的内容,我认为我正确地使用了 Provider 和 Consumer,但我无法确定这是否属实。我欢迎任何见解。

最佳答案

有一些方法可以避免重新渲染,也可以使您的状态管理“类似于 redux”。我将向您展示我是如何做的,它远非 redux,因为 redux 提供了很多实现起来并不简单的功能,比如从任何 Action 或 combineReducers 将 Action 分派(dispatch)给任何 reducer 的能力等等许多其他人。

创建你的reducer

export const initialState = {
...
};

export const reducer = (state, action) => {
...
};

创建您的 ContextProvider 组件

export const AppContext = React.createContext({someDefaultValue})

export function ContextProvider(props) {

const [state, dispatch] = useReducer(reducer, initialState)

const context = {
someValue: state.someValue,
someOtherValue: state.someOtherValue,
setSomeValue: input => dispatch('something'),
}

return (
<AppContext.Provider value={context}>
{props.children}
</AppContext.Provider>
);
}

在应用的顶层或您需要的地方使用您的 ContextProvider

function App(props) {
...
return(
<AppContext>
...
</AppContext>
)
}

将组件写成纯函数式组件

这样他们只会在那些特定的依赖更新为新值时才重新渲染

const MyComponent = React.memo(({
somePropFromContext,
setSomePropFromContext,
otherPropFromContext,
someRegularPropNotFromContext,
}) => {
... // regular component logic
return(
... // regular component return
)
});

具有从上下文中选择 Prop 的功能(如 redux map...)

function select(){
const { someValue, otherValue, setSomeValue } = useContext(AppContext);
return {
somePropFromContext: someValue,
setSomePropFromContext: setSomeValue,
otherPropFromContext: otherValue,
}
}

编写一个 connectToContext HOC

function connectToContext(WrappedComponent, select){
return function(props){
const selectors = select();
return <WrappedComponent {...selectors} {...props}/>
}
}

把它们放在一起

import connectToContext from ...
import AppContext from ...

const MyComponent = React.memo(...
...
)

function select(){
...
}

export default connectToContext(MyComponent, select)

用法

<MyComponent someRegularPropNotFromContext={something} />

//inside MyComponent:
...
<button onClick={input => setSomeValueFromContext(input)}>...
...

我在其他 StackOverflow 问题上做的演示

Demo on codesandbox

避免重新渲染

MyComponent 仅当来自上下文的特定 Prop 更新为新值时才会重新呈现,否则它将保留在那里。select 中的代码将在每次上下文更新的任何值时运行,但它什么都不做并且很便宜。

其他解决方案

我建议检查一下 Preventing rerenders with React.memo and useContext hook.

关于javascript - React Context API 和避免重新渲染,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51317371/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com