简体   繁体   English

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

[英]React: Changing Context in Consumer

The below question relates to the following sections in the React Context documentation:以下问题与 React Context 文档中的以下部分有关:

  1. Dynamic Context动态上下文
  2. Updating Context from a Nested Component 从嵌套组件更新上下文

Disclaimer: Apologies for all the background information below.免责声明:对以下所有背景信息深表歉意。 It provides context and will hopefully be helpful to future visitors .它提供了背景信息,希望对未来的访问者有所帮助


What We Know我们所知道的

  • Link 1链接 1
    • The (default) context value is set to themes.dark (an object that contains two properties: foreground and background ) (默认)上下文值设置为themes.dark (一个包含两个属性的对象: foregroundbackground
    • The default value is only ever used if there are no Providers above the Consumer in the component tree仅当组件树中的Consumer上方没有Providers时才使用默认值
    • In this case, there is a Provider present in the top-level component ( App )在这种情况下,顶级组件 ( App ) 中存在一个Provider
    • This Provider ( App ), passes down its own state as the context value这个Provider ( App ),将它自己的state作为上下文值传递下去
    • It is smart to keep the values provided by a Provider equal in structure and type to the default context value (avoids Consumers getting confused)保持Provider的值在结构和类型上与默认上下文值相同是明智的(避免Consumers感到困惑)
    • Thus, state in the top-level component ( App ) holds an object of the same format as the default context value : themes.light因此,顶级组件 ( App ) 中的state持有与默认上下文值相同格式的对象: themes.light
    • Conclusion from the above: When a Consumer reads the context , it reads App 's state上面的结论:当Consumer读取context 时,它读取App的状态
    • In other words, we are here using context to pass a parent ( App ) state deep down in the component tree, without having to pass it through every component in the middle换句话说,我们在这里使用上下文在组件树的深处传递父( Appstate ,而不必通过中间的每个组件传递它
    • When state in the top-level component ( App ) changes, it re-renders and a new value for state is provided to the Consumer当顶级组件 ( App ) 中的 state 发生变化时,它会重新渲染并将 state 的新值提供给Consumer
    • This way, the Consumer reads the parent's state , via context这样, Consumer通过上下文读取父级的state
    • ... ...
    • Moving on, we see in link 1 that a function to set state ( toggleTheme ) is passed down the component tree as a normal prop继续前进,我们在链接 1 中看到设置state的函数( toggleTheme )作为普通prop向下传递到组件树
    • Thus, in link 1, context only contains an object that reads state因此,在链接 1 中, context包含读取state的对象
    • We are able to set state in the Consumer by passing the setState function as a normal prop from the Provider 's child, down through all the intermediate components, and in to the Consumer我们能够在设定状态Consumer通过传递setState功能作为一个正常的propProvider的孩子,向下通过所有的中间组件,以及向Consumer
    • Setting the state in the top-level component ( App ), leads to a re-render of itself, which leads to a re-render of the Provider , which then passes the new App state value down to its Consumer via context在顶级组件 ( App ) 中设置state会导致自身重新呈现,从而导致重新呈现Provider ,然后将新的App state值通过上下文传递给它的Consumer
    • As such, the Consumer always knows App 's state, via context因此, Consumer总是通过上下文知道App的状态
    • In conclusion, the flow is:总之,流程是:
      1. Parent's state is provided as context value to child Consumer (s)state作为上下文值提供给子Consumer
      2. Parent's state is updated by some child某些孩子更新了父母的state
      3. Parent re-renders父级重新渲染
      4. Provider sees that context value ( App 's state ) has changed, and re-renders all its Consumer s with the new value Provider看到上下文值Appstate )已更改,并使用新值重新呈现其所有Consumer
  • Link 2链接 2
    • In link 2, we set state in the Consumer , by passing the setState function within the context在链接 2 中,我们通过在上下文中传递setState函数来在Consumer设置state
    • This differs from link 1, where we relied on a normal prop to set state这与链接 1 不同,链接 1 中我们依靠普通prop来设置state

Questions问题

We know from the docs that:我们从文档中知道:

Every Context object comes with a Provider React component that allows consuming components to subscribe to context changes....每个 Context 对象都带有一个 Provider React 组件,该组件允许使用组件订阅上下文更改......

All consumers that are descendants of a Provider will re-render whenever the Provider's value prop changes.每当 Provider 的 value 属性发生变化时,所有作为 Provider 后代的消费者都会重新渲染。

  1. Let's assume we use a normal variable in App as the context value .假设我们在App使用一个普通变量作为上下文值 We know from the above quote that changing it leads to the Provider re-rendering.我们从上面的引用中知道,更改它会导致Provider重新渲染。 Why then, do we bother using state as the context value?那么,我们为什么要费心使用state作为上下文值呢? What is the benefit of that, vs. just using any normal variable in App ?与仅在App使用任何普通变量相比,这样做有什么好处?
  2. Both the two approaches above allow us to update state .以上两种方法都允许我们更新state Why is link 2 incorporating the function to update state within state itself?为什么链接 2 包含state本身更新state的功能? Could we not just have it as a separate setState function, which is passed to the Consumer via context in an object that has two properties (one is state and the other is the standalone function to update state )?我们能否将它作为一个单独的setState函数,通过具有两个属性(一个是state ,另一个是更新state的独立函数)的对象中的上下文传递给Consumer

Let's assume we use a normal variable in App as the context value.假设我们在 App 中使用一个普通变量作为上下文值。 We know from the above quote that changing it leads to the Provider re-rendering.我们从上面的引用中知道,更改它会导致 Provider 重新渲染。 Why then, do we bother using state as the context value?那么,我们为什么要使用状态作为上下文值呢? What is the benefit of that, vs. just using any normal variable in App?与仅在 App 中使用任何普通变量相比,这样做有什么好处?

It's true that when the provider is rerendered with a changed value, any descendents that care about the context will rerender.确实,当使用更改的值重新呈现提供者时,任何关心上下文的后代都会重新呈现。 But you need something to cause the provider to rerender in the first place.但是您首先需要一些东西来使提供者重新呈现。 This will happen when App's state or its props change (or when you call forceUpdate, but don't do that).当 App 的 state 或其 props 发生变化时(或者当你调用 forceUpdate,但不要那样做)时,就会发生这种情况。 Presumably, this is at the top of your application, so there are no props coming in, which means you'll use state to cause it to rerender.据推测,这是在您的应用程序的顶部,所以没有道具进来,这意味着您将使用 state 使其重新渲染。

Both the two approaches above allow us to update state.上述两种方法都允许我们更新状态。 Why is link 2 incorporating the function to update state within state itself?为什么链接 2 包含在状态本身内更新状态的功能? Could we not just have it as a separate setState function, which is passed to the Consumer via context in an object that has two properties (one is state and the other is the standalone function to update state)?我们能否将它作为一个单独的 setState 函数,通过具有两个属性(一个是状态,另一个是更新状态的独立函数)的对象中的上下文传递给消费者?

When deciding whether to rerender descendants due to a change of context, react will do basically a === between the old value and the new value.当决定是否由于上下文的变化而重新渲染后代时,react 基本上会在旧值和新值之间做一个=== This is super quick and works well with React's preference for immutable data, but when using objects as your value you need to be careful that you're not making new objects on every render.这非常快,并且适用于 React 对不可变数据的偏好,但是当使用对象作为您的值时,您需要小心不要在每次渲染时都创建新对象。 For example, if App is doing something like the following, it will be creating a brand new object every time it renders, and thus will be forcing all the context consumers to rerender as well:例如,如果 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
      }} />
    )
  }
}

So the example where they store the function in state is to make sure that the entire value they're providing does not change from one render to another.因此,他们将函数存储在状态中的示例是确保他们提供的整个值不会从一个渲染更改为另一个渲染。

Let's assume we use a normal variable in App as the context value.假设我们在 App 中使用一个普通变量作为上下文值。 We know from the above quote that changing it leads to the Provider re-rendering.我们从上面的引用中知道,更改它会导致 Provider 重新渲染。 Why then, do we bother using state as the context value?那么,我们为什么要使用状态作为上下文值呢? What is the benefit of that, vs. just using any normal variable in App?与仅在 App 中使用任何普通变量相比,这样做有什么好处?

When you use state and update it - it does not matter at all if you are using provider or not - all the provider and components under it will update.当你使用 state 并更新它时——不管你是否使用提供者——所有的提供者和它下面的组件都会更新。 Thats under official React Context documentation and is wrong.那是在官方 React Context 文档下,是错误的。 It means changing provider values DOES NOT call consumer updates at all.这意味着更改提供者值根本不会调用消费者更新。

You can validate this by making a separate component with state (which would not be inside provider) and assign that state variable to the provider.您可以通过创建一个带有状态的单独组件(不会在提供者内部)来验证这一点,并将该状态变量分配给提供者。 So when component state changes, value in state changes and in turn provider should notice this and update consumer.因此,当组件状态发生变化时,状态中的值发生变化,提供者应该注意到这一点并更新消费者。 Which it is NOT doing.它没有做。

In order to update components under consumers, you, unfortunately, have to do it manually.不幸的是,为了更新消费者下的组件,您必须手动进行。 Unless your intension is to update everything under the provider.除非您的意图是更新提供程序下的所有内容。

This is true as of 2021-04-21 under React 17.0.2 - provider value changes are not being monitored and consumers are not being updated sadly.在 React 17.0.2 下,截至 2021 年 4 月 21 日,情况确实如此——提供者值的变化没有被监控,消费者也没有被更新。 Unless you put all your provider in component with state, but changing its state forces updating all components under the provider.除非您将所有提供程序都放在带有状态的组件中,但更改其状态会强制更新提供程序下的所有组件。 Sadly.可悲。

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

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