React:在消费者中改变上下文

IT技术 javascript reactjs
2021-05-10 09:57:13

以下问题与 React Context 文档中的以下部分有关:

  1. 动态上下文
  2. 从嵌套组件更新上下文

免责声明:对以下所有背景信息深表歉意。它提供了背景信息,希望对未来的访问者有所帮助


我们所知道的

  • 链接 1
    • (默认)上下文值设置为themes.dark(包含两个属性的对象:foregroundbackground
    • 默认值仅在组件树中没有Providers上方时才使用Consumer
    • 在这种情况下,Provider顶级组件中有一个礼物 ( App)
    • this Provider( App),将它自己state作为上下文值传递下去
    • Provider结构和类型提供的值保持为默认上下文值是明智的(避免Consumers混淆)
    • 因此,state在顶级组件 ( App) 中保存了一个与默认上下文值相同格式的对象themes.light
    • 上面的结论:当 aConsumer读取上下文时,它读取App的状态
    • 换句话说,我们在这里使用上下文在组件树的深处传递父(Appstate,而不必将其传递到中间的每个组件
    • 当顶级组件 ( App) 中的状态发生变化时,它会重新渲染并为状态提供一个新值给Consumer
    • 这样,通过上下文Consumer读取父级的state
    • ...
    • 继续前进,我们在链接 1 中看到设置 state( toggleTheme)的函数作为正常情况沿组件树向下传递prop
    • 因此,在链接 1 中,context 包含一个读取的对象 state
    • 我们能够设定的状态Consumer通过将setState功能作为一个正常的propProvider的孩子,向下通过所有的中间组件,以及向Consumer
    • state在顶级组件 ( App) 中设置 the会导致自身重新渲染,从而导致 重新渲染Provider,然后将新App state值向下传递给它的Consumervia上下文
    • 因此,Consumer总是App通过上下文知道的状态
    • 总之,流程是:
      1. 家长state提供的上下文值以孩子Consumer(一个或多个)
      2. 家长state是孩子的一些更新
      3. 父级重新渲染
      4. Provider看到上下文值( App's state) 已更改,并Consumer使用新值重新呈现其所有s
  • 链接 2
    • 在链接 2 中,我们通过上下文中传递函数来设置 state,ConsumersetState
    • 这与链接 1 不同,链接 1 中我们依靠法线prop来设置state

问题

我们从文档中知道:

每个 Context 对象都带有一个 Provider React 组件,该组件允许使用组件订阅上下文更改......

每当 Provider 的 value 属性发生变化时,所有作为 Provider 后代的消费者都会重新渲染。

  1. 假设我们使用普通变量 inApp作为上下文值我们从上面的引用中知道,改变它会导致Provider重新渲染。那么,为什么我们懒得使用state作为背景值?与仅在 中使用任何正常变量相比,这样做有什么好处App
  2. 上述两种方法都允许我们更新state. 为什么链接 2 包含要state state自身内部更新的功能我们能否将它作为一个单独的setState函数传递给具有两个属性的对象中Consumervia上下文(一个是state,另一个是要更新的独立函数state)?
2个回答

假设我们在 App 中使用一个普通变量作为上下文值。我们从上面的引用中知道,更改它会导致 Provider 重新渲染。那么,为什么我们要费心使用状态作为上下文值呢?与仅在 App 中使用任何普通变量相比,这样做有什么好处?

确实,当使用更改的值重新呈现提供者时,任何关心上下文的后代都会重新呈现。但是您首先需要一些东西来使提供者重新呈现。当应用程序的状态或其props发生变化时(或者当您调用 forceUpdate 但不要这样做时),就会发生这种情况。据推测,这是在您的应用程序的顶部,所以没有props进来,这意味着您将使用 state 使其重新渲染。

上述两种方法都允许我们更新状态。为什么链接 2 包含在状态本身内更新状态的功能?我们能否将它作为一个单独的 setState 函数,通过具有两个属性(一个是状态,另一个是更新状态的独立函数)的对象中的上下文传递给消费者?

当决定是否由于上下文的变化而重新渲染后代时,react 基本上会===在旧值和新值之间做一个这非常快,并且适用于 React 对不可变数据的偏好,但是当使用对象作为您的值时,您需要小心不要在每次渲染时都创建新对象。例如,如果 App 正在做类似下面的事情,它会在每次渲染时创建一个全新的对象,因此将强制所有上下文消费者也重新渲染:

class App extends Component {
  state = {
    data: { 
      hello: 'world',
    }
  }

  updateData() {
    // some function for updating the state  
  }

  render() {
    return (
      <MyContext.Provider value={{ 
        data: this.state.data, 
        updateData: this.updateData
      }} />
    )
  }
}

因此,他们将函数存储在状态中的示例是确保他们提供的整个值不会从一个渲染更改为另一个渲染。

假设我们在 App 中使用一个普通变量作为上下文值。我们从上面的引用中知道,更改它会导致 Provider 重新渲染。那么,为什么我们要费心使用状态作为上下文值呢?与仅在 App 中使用任何普通变量相比,这样做有什么好处?

当你使用 state 并更新它时——不管你是否使用提供者——所有的提供者和它下面的组件都会更新。那是在官方 React Context 文档下,这是错误的。这意味着更改提供者值根本不会调用消费者更新。

您可以通过创建一个带有状态的单独组件(不会在提供者内部)来验证这一点,并将该状态变量分配给提供者。因此,当组件状态发生变化时,状态中的值发生变化,而提供者应该注意到这一点并更新消费者。它没有做。

不幸的是,为了更新消费者下的组件,您必须手动进行。除非您的意图是更新提供程序下的所有内容。

在 React 17.0.2 下,截至 2021 年 4 月 21 日,情况确实如此——提供者值的变化没有受到监控,消费者也没有更新。除非您将所有提供程序都放在带有状态的组件中,但更改其状态会强制更新提供程序下的所有组件。可悲。