简体   繁体   English

为什么 React Context.Provider 是必需的(或有用的)?

[英]Why Is React Context.Provider Necessary (Or useful)?

The reason React has contexts is to allow for multiple sibling components to share a piece of state-data. React 有上下文的原因是允许多个兄弟组件共享一个状态数据。 It is the go-to method for allowing two unrelated components to read/write in shared variables.这是允许两个不相关的组件在共享变量中读/写的首选方法。 The reason it is necessary is that React has no way to easily source a data value to multiple screens without actually passing that data between screens.之所以有必要,是因为 React 无法在不实际在屏幕之间传递数据的情况下轻松地将数据值提供给多个屏幕。 Instead, it allows each screen access to the data when it needs it.相反,它允许每个屏幕在需要时访问数据。

So... The implementation requires that a component be created, called a Context.Provider component, and then you have to wrap the components who need access to the shared data inside the Context.Provider .所以...实现需要创建一个组件,称为Context.Provider组件,然后您必须将需要访问共享数据的组件包装在Context.Provider中。 But why?但为什么? Why on earth is that a requirement?为什么这是一个要求? Context s are designed sharing data between components who aren't hierarchally related, and were required to put the components within a heirarchy to do so? Context旨在在不具有层次关系的组件之间共享数据,并且需要将组件置于层次结构中才能这样做?

It would be 100 times more straight forward and just as effective to simply drop the requirement of using a Context.Provider, simple have the useContext function give access to a set variable by default:简单地放弃使用 Context.Provider 的要求会更直接 100 倍并且同样有效,简单地让useContext函数默认提供对 set 变量的访问:

// In ctx.js
import React from 'react';
export default CTX = React.createContext({val: "value"});
// In compA.js
import CTX from './ctx.js';
import {useContext} from 'react';
function A(props) {
    var [context, setContext] = useContext(CTX);
    console.log(context); //Logs {val: 'value'};
    setContext({val: "newValue"});
}

Then later on, assuming component B renders after A:然后稍后,假设组件 B 在 A 之后呈现:

import CTX from './ctx.js';
import {useContext} from 'react';
function B(props) {
    var [context, setContext] = useContext(CTX);
    console.log(context); //Logs {val: 'newValue'};
}

The above usage, if it actually worked, solves the task of "sharing data between unrelated components", and is much much simpler than requiring an entire new component be defined in the context file.上面的用法,如果它确实有效,解决了“在不相关的组件之间共享数据”的任务,并且比要求在上下文文件中定义一个全新的组件要简单得多。 This solution is better because: 1. No required restructuring of the application.该解决方案更好,因为: 1. 不需要对应用程序进行重构。 You don't need to wrap components in a provider.您不需要将组件包装在提供者中。 2. Any Components can just ask for any shared state easily, and they can set the shared state easily. 2.任何组件都可以轻松地请求任何共享状态,并且可以轻松地设置共享状态。 3. Easier to understand with much less code involved (One line of code for import and one line to initiate the context). 3. 代码更少,更容易理解(一行代码用于导入,一行代码用于启动上下文)。 4. Doesn't sacrifice anything. 4. 不牺牲任何东西。 This method allows for easy sharing of state between components, which is the entire reason of contexts in the first place.这种方法允许在组件之间轻松共享状态,这首先是上下文的全部原因。

Am I crazy?我疯了吗? Is there a legitamate reason that we'd absolutely need to wrap our components up in a special component to share data?.. Why can't the shared state just exist independently?我们绝对需要将我们的组件包装在一个特殊的组件中以共享数据是否有正当理由?..为什么共享状态不能独立存在? Its like they chose a bad solution... Why make every developer wrap there components in another component before using shared state, why not just let the developer use the damned shared state when they need to use it instead of jumping through a hoop?就像他们选择了一个糟糕的解决方案一样……为什么让每个开发人员在使用共享状态之前将组件包装在另一个组件中,为什么不让开发人员在需要使用该死的共享状态时使用它而不是跳过一个箍? Someone please educate me.有人请教育我。

Edit: One answer said that with my described method we wouldn't be able to access multiple contexts with a single component.编辑:一个答案说,使用我描述的方法,我们将无法使用单个组件访问多个上下文。 That is false.那是错误的。 It is actually easier with my described method:使用我描述的方法实际上更容易:

// In context.js
export const CTX = React.createContext({val: "val"});
export const CTX2 = React.createContext({val2: "val2"});
// In app.js

function App(props) {
    const [state, setState] = useContext(CTX);
    const [state2, setState2] = userContext(CTX2);
    return (<></>);
}

Easy.简单。 No need for Context.Provider .不需要Context.Provider This is multiple contexts being used in one component, requiring just two calls to useContext versus wrapping your entire application in two nested contexts, which is what is what you have to do with current Context.Provider method...这是在一个组件中使用的多个上下文,只需要两次调用 useContext 而不是将整个应用程序包装在两个嵌套的上下文中,这就是您必须使用当前Context.Provider方法所做的...

Mate, answer is simple.伙计,答案很简单。 React component only re-renders when it's props or state changes. React 组件仅在 props 或 state 更改时重新渲染。 Without Context.Provider component react will never understand when to re-render child components, thus you will have stale, render-blocked components.如果没有 Context.Provider 组件,react 将永远无法理解何时重新渲染子组件,因此您将拥有陈旧的、被渲染阻止的组件。

The purpose for having a Context Provider wrap around children is to keep track of state and props, read on how state and props between parents and children affect each other.让 Context Provider 环绕孩子的目的是跟踪状态和道具,阅读父母和孩子之间的状态和道具如何相互影响。 If there was no way for the Context Provider to keep track of its children, how would the components that use the Context be able to update(Changing parent state affects children, so there may be rerendering).如果 Context Provider 无法跟踪其子项,那么使用 Context 的组件将如何更新(更改父状态会影响子项,因此可能需要重新渲染)。

It's also important to understand React's philosophy and it's focus on components, it is a component-based library after all.理解 React 的哲学也很重要,它专注于组件,毕竟它是一个基于组件的库。

Important thing to remember: Parent state change will affect children, so if state changes in parent, children components will be reevaluated and depending on how your components, state, and data are optimized (memo, callback, etc.) a rerender may occur, thus updating those children components as well.重要的是要记住:父状态的变化会影响子组件,所以如果父组件的状态发生变化,子组件将被重新评估,并且根据你的组件、状态和数据的优化方式(备忘录、回调等),可能会发生重新渲染,从而也更新这些子组件。

have been struggle for this question until i found this https://github.com/pmndrs/zustand i create single instance for each store and no need Provider, i think this is what we want一直在为这个问题而苦苦挣扎,直到我找到这个https://github.com/pmndrs/zustand我为每个商店创建了一个实例,不需要 Provider,我认为这就是我们想要的

The reason React has contexts is to allow for multiple sibling components to share a piece of state-data. React具有上下文的原因是允许多个兄弟组件共享一条状态数据。 It is the go-to method for allowing two unrelated components to read/write in shared variables.这是允许两个不相关的组件读取/写入共享变量的首选方法。 The reason it is necessary is that React has no way to easily source a data value to multiple screens without actually passing that data between screens.之所以必须这样做,是因为React无法轻松地将数据值提供给多个屏幕,而无需在屏幕之间实际传递数据。 Instead, it allows each screen access to the data when it needs it.相反,它允许每个屏幕在需要时访问数据。

So... The implementation requires that a component be created, called a Context.Provider component, and then you have to wrap the components who need access to the shared data inside the Context.Provider .所以...实现需要创建一个称为Context.Provider组件的组件,然后必须将需要访问共享数据的组件包装在Context.Provider But why?但为什么? Why on earth is that a requirement?为什么这是一项要求? Context s are designed sharing data between components who aren't hierarchally related, and were required to put the components within a heirarchy to do so? Context被设计为在没有层次关系的组件之间共享数据,并且需要将这些组件置于层次结构中吗?

It would be 100 times more straight forward and just as effective to simply drop the requirement of using a Context.Provider, simple have the useContext function give access to a set variable by default:直接降低使用Context.Provider的要求将是100倍,并且同样有效,简单地让useContext函数默认情况下可以访问set变量:

// In ctx.js
import React from 'react';
export default CTX = React.createContext({val: "value"});
// In compA.js
import CTX from './ctx.js';
import {useContext} from 'react';
function A(props) {
    var [context, setContext] = useContext(CTX);
    console.log(context); //Logs {val: 'value'};
    setContext({val: "newValue"});
}

Then later on, assuming component B renders after A:然后稍后,假设组件B在A之后渲染:

import CTX from './ctx.js';
import {useContext} from 'react';
function B(props) {
    var [context, setContext] = useContext(CTX);
    console.log(context); //Logs {val: 'newValue'};
}

The above usage, if it actually worked, solves the task of "sharing data between unrelated components", and is much much simpler than requiring an entire new component be defined in the context file.上面的用法(如果确实有效)解决了“在不相关的组件之间共享数据”的任务,并且比在上下文文件中定义整个新组件要简单得多。 This solution is better because: 1. No required restructuring of the application.此解决方案更好,因为:1.无需重组应用程序。 You don't need to wrap components in a provider.您不需要将组件包装在提供程序中。 2. Any Components can just ask for any shared state easily, and they can set the shared state easily. 2.任何组件都可以轻松地请求任何共享状态,并且可以轻松设置共享状态。 3. Easier to understand with much less code involved (One line of code for import and one line to initiate the context). 3.所涉及的代码更少,更易于理解(一行代码用于导入,而一行代码用于启动上下文)。 4. Doesn't sacrifice anything. 4.不牺牲任何东西。 This method allows for easy sharing of state between components, which is the entire reason of contexts in the first place.此方法允许在组件之间轻松共享状态,这首先是上下文的全部原因。

Am I crazy?我疯了吗? Is there a legitamate reason that we'd absolutely need to wrap our components up in a special component to share data?.. Why can't the shared state just exist independently?是否有正当理由,我们绝对需要将我们的组件包装在一个特殊的组件中才能共享数据?..为什么共享状态不能仅仅独立存在? Its like they chose a bad solution... Why make every developer wrap there components in another component before using shared state, why not just let the developer use the damned shared state when they need to use it instead of jumping through a hoop?就像他们选择了一个错误的解决方案一样……为什么要让每个开发人员在使用共享状态之前将组件包装在另一个组件中,为什么不让开发人员在需要使用共享状态时使用该共享状态,而不是跳了圈? Someone please educate me.有人请教育我。

Edit: One answer said that with my described method we wouldn't be able to access multiple contexts with a single component.编辑:一个答案说,用我描述的方法,我们将无法使用单个组件访问多个上下文。 That is false.那是错误的。 It is actually easier with my described method:使用我描述的方法实际上更容易:

// In context.js
export const CTX = React.createContext({val: "val"});
export const CTX2 = React.createContext({val2: "val2"});
// In app.js

function App(props) {
    const [state, setState] = useContext(CTX);
    const [state2, setState2] = userContext(CTX2);
    return (<></>);
}

Easy.简单的。 No need for Context.Provider .不需要Context.Provider This is multiple contexts being used in one component, requiring just two calls to useContext versus wrapping your entire application in two nested contexts, which is what is what you have to do with current Context.Provider method...这是在一个组件中使用的多个上下文,只需要对useContext的两次调用,而不是将整个应用程序包装在两个嵌套的上下文中,这就是当前Context.Provider方法所要做的...

Contexts Are Made To Handle All Use Cases上下文是为了处理所有用例

I've since spent more time using Contexts in my applications and have come to realize that Context.Provider is quite useful in a variety of situations.从那以后,我花了更多时间在我的应用程序中使用 Contexts,并逐渐意识到Context.Provider在各种情况下都非常有用。 My initial complaint has merit in that often times when using Context we are simply wanting a variant of state that can be shared between components.我最初的抱怨是有道理的,因为在使用Context我们经常只是想要一种可以在组件之间共享的状态变体。 In this common use case, Context.Provider does indeed requires us to write a bit of unnecessary boilerplate code and requires us to wrap elements in the provider so that they have access to the context.在这个常见的用例中, Context.Provider确实需要我们编写一些不必要的样板代码,并要求我们将元素包装在提供者中,以便它们可以访问上下文。

However any time our shared state becomes a little more complicated having a dedicated Context.Provider component can make our lives a lot easier.然而,任何时候我们的共享状态变得有点复杂,拥有一个专用的Context.Provider组件可以让我们的生活更轻松。 Here is a use case to consider这是一个需要考虑的用例

Shared Data From External Sources (Post, Get)来自外部来源的共享数据(发布、获取)

Contexts may allow us to store any code related to the initialization of the shared state within the context itself, resulting in more easily readable and maintainable code.上下文可能允许我们在上下文本身中存储与共享状态初始化相关的任何代码,从而使代码更易于阅读和维护。 For example, lets say we have some user text posts on our server that are displayed by multiple components within our application, and we would also like for our users to be able to add new posts.例如,假设我们的服务器上有一些用户文本帖子,这些帖子由我们应用程序中的多个组件显示,并且我们还希望我们的用户能够添加新帖子。 All of this can be handled quite neatly within the Context.Provider :所有这些都可以在Context.Provider非常巧妙地处理:

import React, {useContext, useEffect, useState} from 'react';
export const PostsContext = React.createContext([]);

export default PostsContextProvider({children}) {
    const [posts, setPosts] = useState([]);
    
    function fetchPosts() {
        // Here we will fetch the posts from our API, and then set the state
        // stored within the Context.Provider equal to the fetched posts.
        fetch('https://www.fakewebsite.com/api/posts/get', {
            method: 'GET',
            headers: {'Content-Type': 'application/json'}
        }).then((response)=>{
            // Convert response to json
            return response.json();
        }).then((json)=>{
            // json here is the posts we fetched from the server, so we set the state
            // equal to this value. This will update the state within all components
            // that are using the context.
            setPosts(json.posts);
        })
    }

    useEffect(function(){
        // This function will run a single time when the application is started
        fetchPosts();
    },[])

    function addNewPost(post) {
        // This is the function that will be used by the components.
        // First, we will send the new post to the server so that it can store it.
        fetch('https://www.fakewebsite.com/api/posts/post', {
            method: "POST",
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({post: post})
        }).then((response)=>{
            if(response.ok) {
                // The server has updated its database with our new post.
                // Now we just need to fetch the posts from the server again to get the updated data.
                fetchPosts();
            } 
        })
    }
    return (
        <PostsContext.Provider
            value={[posts, addNewPost]}
        >
            {children}
        <PostsContext.Provider />
    )
}

Notice that the value prop we are passing does not actually pass the state setter function directly.请注意,我们传递的value prop 实际上并没有直接传递 state setter 函数。 Instead, we pass the addNewPost function.相反,我们传递addNewPost函数。 So, when a component calls useContext(PostsContext) they will get the addNewPost function.因此,当组件调用useContext(PostsContext)它们将获得addNewPost函数。 This is extremely useful, it will allow our components to easily add a single post to the shared state, while also handling the server update!这非常有用,它将允许我们的组件轻松地将单个帖子添加到共享状态,同时还可以处理服务器更新! Very cool.很酷。 With the solution I originally proposed, this would be impossible, because we would only ever get a simple state setting function from our useContext call.使用我最初提出的解决方案,这是不可能的,因为我们只能从useContext调用中获得一个简单的状态设置函数。

Now, we must wrap our application in the provider to make it available to all components:现在,我们必须将我们的应用程序包装在提供程序中以使其可用于所有组件:

// App.js

import React from 'react';
import PostsContextProvider from './posts_context';
import MyComponent from './my_component';
import MyOtherComponent from './my_other_component';

export default function App() {
    return (
        <PostsContextProvider>
            <MyComponent/>
            <MyOtherComponent/>
        </PostsContextProvider>
    )
}

At this point, MyComponent and MyOtherComponent now have access to the context using the useContext hook.此时, MyComponentMyOtherComponent现在可以使用useContext钩子访问上下文。 It is now extremely simple for the components to access the posts data and also update it with a new post.现在组件访问帖子数据并使用新帖子更新它非常简单。

import React, {useContext} from 'react';
import {PostContext} from './posts_context';
export default function MyComponent() {
    const [posts, addPost] = useContext(PostsContext); // 'posts' will always be up to date with the latest data thanks to the context.
    
    ...
}
import React, {useContext} from 'react';
import {PostContext} from './posts_context';
export default function MyOtherComponent() {
    const [posts, addPost] = useContext(PostsContext); 
    
    ...
    function handleAddPost(title, text) {
        // Now when this component wants to add a new post, 
        // we just use the `addPost` function from the context.
        addPost({title, text});
    }
    ...
}

The beauty of this is that all the code related to the fetching and posting of data can be neatly contained within the provider, separated from the UI code.这样做的好处是所有与获取和发布数据相关的代码都可以整齐地包含在提供程序中,与 UI 代码分开。 Each component has easy access to the posts data, and when either component adds a new post the other component will be updated with the new data.每个组件都可以轻松访问posts数据,当其中一个组件添加新帖子时,另一个组件将使用新数据进行更新。

Final Thoughts最后的想法

This is just scratching the surface of the usefulness of Context.Provider .这只是Context.Provider有用性的Context.Provider It's easy to imagine using a Context.Provider to handle persistent data storage using a method very similar to the above, replacing the fetch calls with function that store/fetch persistent data.很容易想象使用Context.Provider使用与上述非常相似的方法来处理持久数据存储,用存储/获取持久数据的函数替换fetch调用。 Or even, some combination of persistent data and fetched data.或者甚至是持久数据和获取数据的某种组合。

Upon revisiting my original question, it actually made me laugh.在重新审视我最初的问题时,它实际上让我发笑。 I was sort of right, there should perhaps be a way to handle simple shared state between components that does not require wrapping components in a provider and does not require any provider code at all.我是对的,也许应该有一种方法来处理组件之间的简单共享状态,不需要将组件包装在提供者中,也根本不需要任何提供者代码。 However, providers are just so dang useful in any kind of state management within an application that it is actually probably a good thing to force people to use them for simple shared state, because then they will have to learn about this wonderful tool.然而,提供者在应用程序中的任何类型的状态管理中都非常有用,迫使人们将它们用于简单的共享状态实际上可能是一件好事,因为那样他们将不得不了解这个很棒的工具。

暂无
暂无

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

相关问题 React context.provider不会更改默认值 - React context.provider doesn't change the default value 在 React 中将多个值和设置器对传递给 Context.Provider - Passing multiple value and setter pairs to Context.Provider in React 如何在 react 和 typescript 中使用 context.provider 设置 state? - How to set the state using context.provider in react and typescript? React Native Context - 从渲染之外的 Context.provider 中检索值 function - React Native Context - retrieve the value from Context.provider outside of the render function React - value 属性是必需的<Context.Provider> . 你是拼错了还是忘记通过了? - React - The value prop is required for the <Context.Provider>. Did you misspell it or forget to pass it? 如何访问react Context.Provider值内的另一个函数? - How can I access another function inside the react Context.Provider value? 我正在尝试使用挂钩来管理Context.Provider的状态 - I am attempting to use hooks to manage the state of Context.Provider 用它包装我的应用程序组件时 Context.provider 抛出错误 - Context.provider throw error when wrapping my app component with it 如何创建类似“ context.Provider” /“ context.Consumer”的结构以在机器人应用中传递值? - How to create a `context.Provider`/`context.Consumer`-like structure to pass values in a bot app? Context.Provider 和 Context.Consumer 和 JavaScript 中的 document.querySelector() 一样吗? - Is Context.Provider and Context.Consumer the same as document.querySelector() in JavaScript?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM