React中的数据传递----上下文(Context)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React中的数据传递----上下文(Context)相关的知识,希望对你有一定的参考价值。

参考技术A 官方文档:Context 旨在共享一个组件树内可被视为 “全局” 的数据,例如当前经过身份验证的用户,主题或首选语言等。

下面是一个简单的上下文使用实例:最外层组件一个color,需要传递给Title组件和Content组件(根组件,绕过子组件,传给孙组件),如下图

基本组件结构如下:

Header和Main

Title和Content

分一下几步:

这样传递过来,Title和Content就拿到this.context对象,里面包含color属性,则能在Title和Content中使用了

通过以上,就绕过Header和Main,直接将根节点的属性传递到了孙组件Title和Content里面,这样子组件谁想要谁接收这个属性就行了就行了。

比如在Content组件, 注意:这个属性是根节点的状态,状态是自能自己改的。所以我们需要在根组件,也就是HomePage定义一个个方法,谁需要改,就把这个方法当属性传给谁。
HomePage:

接下来在需要改变这个属性子组件接受这个方法:并在事件中调用context的方法

如何将 React Context 中的值传递给 Redux-Saga 函数?

【中文标题】如何将 React Context 中的值传递给 Redux-Saga 函数?【英文标题】:How to pass a value from React Context into a Redux-Saga function? 【发布时间】:2022-01-13 03:22:06 【问题描述】:

如何将 React 上下文中的内容传递到我的 Redux Saga 函数中?

我有一个上下文值,我可以用类似这样的东西在反应组件中检索它

const  connectionObject  = useMyContext();

这将允许我从上下文中获取 connectionObject 实例。

不过,我还在我的应用程序的其他一些部分使用了 redux saga。这些 redux sagas 之一将需要使用上下文中的 personObject

由于 Redux-Sagas 本身在技术上只是常规的生成器函数,因此我不能在其中使用 useMyContext() 来检索 connectionObject。例如,这是行不通的

export function* saveData() 
   const  connectionObject  = useMyContext();  // This will not work
   yield performSaveSomething(connectionObject);

Redux Saga 是由我从 react 组件中调度 redux 操作触发的。一种可能的解决方法当然是从 react 组件的上下文中获取 connectionObject,然后使用 connectionObject 作为有效负载的一部分分派操作,然后将其传递给 saga。但这感觉完全是反模式并且并不优雅,尤其是当有效载荷应该更新到状态但在这种情况下仅用于传递给 saga 时。下面是一个例子来说明尴尬:

// In the React component
export const myReactComponent = (props) => 
   const  connectionObject  = useMyContext();
   dispatch(saveDataAction( 
      data: 'some data here to be put in the redux store',
      connection: connectionObject 
   ));


// In the reducer
function reducer (state, action) 
   switch (action.type) 
      case SAVE_DATA_ACTION_TYPE:
         // action.payload has 2 attributes in this case: data and connection
         // But I am throwing away the connection one here in this reducer because that is only meant for the saga. Feels weird here.
         return 
            ...state,
            data: action.payload.data.  // Instead of using the whole payload, I've to use only the "data" attribute of the payload because only this mattered to the store. I can't use the whole payload because it has the connectionObject in there. Doesn't look elegant.
         
   


// And finally in the saga function
export function* saveData( payload:  connection  ) 
   // Good thing here is I could finally access the connectionObject which came from the react context
   // However, it's also doesn't seem right that I'm passing the connectionObject through the payload which isn't a piece of data but as a dependency
   yield performSaveSomething(connection);

   

有没有一种方法可以优雅而轻松地传递从 React 上下文中检索到的值,并以某种方式将其传递给我的 Redux-Saga 函数以便它可以使用它?

【问题讨论】:

两个旁注:首先,基于那个 reducer 示例,看起来您正在“手动”编写 Redux 逻辑。我们强烈建议改用our official Redux Toolkit package,这将大大简化您的 Redux 代码。其次,我们实际上建议反对在大多数用例中使用 saga。它们是复杂异步工作流的绝佳工具,但大多数 Redux 应用不需要它们——尤其是对于基本的数据获取场景。 @markerikson 感谢您的提示!只是想知道,对于大多数用例,您建议使用什么来代替 saga?那会是 redux thunk 吗? 是的,我们通常建议使用 thunk 作为 API 调用的标准方法,或者我们新的 RTK 查询数据获取 API。请参阅the "Redux Essentials" docs tutorial 了解这两种情况的示例。 【参考方案1】:

我的第一个倾向是按照您的建议去做:将其包含在操作的有效负载中。组件知道值,saga 需要值,因此它可以将值传入。这可能是最简单的解决方案。

除此之外...这有点取决于上下文值的性质。让我们从最简单的情况开始,然后转向更困难的情况。最简单的情况:上下文只有一个副本,其值永远不会改变。如果它永远不会改变,那么它实际上不需要在上下文中。您可以简单地将 connectionObject 移动到一个文件中,将其导出,然后在需要的任何地方导入它。


但是您使用的是上下文,所以它可能是一个确实会改变的值。所以这把我们带到了下一个案例:只有一个副本,它的值确实改变了,但只有组件关心它何时改变(saga 只会在需要时查找它,而不需要通知)。为此,我可能会让上下文提供者将其状态复制到一个全局变量,由 saga 检查。

let globalValue = ;
export const getGlobalValue = () => globalValue;

export const MyContextProvider = ( children ) => 
  const [stateValue, setStateValue] = useState();
  useEffect(() => 
    globalValue = stateValue;
  , [stateValue]);

  return (
    <MyContext.Provider value=stateValue>
      children
    </MyContext.Provider>
  )


// In the saga:
import  getGlobalValue  from './path/to/context/provider/file

export function* saveData() 
   const connectionObject = getGlobalValue();
   yield performSaveSomething(connectionObject);


接下来的困难是只有一个副本,值发生变化,组件和 sagas 都需要能够监听其值的变化。例如,也许 saga 启动,检查值,如果它发现值为 false,则需要休眠,直到值变为 true。

为了支持这一点,您将需要有某种 saga 可以订阅的事件发射器。此类代码有许多现有的库,您可以自己滚动,例如:

let listeners = [];
export const subscribe = (callback) => 
  listeners.push(callback);
  const unsubscribe = () => 
    let i = listeners.indexOf(callback);
    listeners.splice(i, 1);
  
  return unsubscribe;


export const MyContextProvider = ( children ) => 
  const [stateValue, setStateValue] = useState();
  useEffect(() => 
    for (const listener of [...listeners]) 
      callback(stateValue);
    
  , [stateValue]);
  // ...


最后,最难的情况是:有多个上下文提供者。如果是这种情况,那么就有多个可能的值,并且 saga 无法知道使用哪一个。当然只能告诉它,你会通过 action payload 做什么,然后让我们回到最初的想法。

我想可以不传入整个对象,而只传递一个 id,然后使用上述技术之一来查找对象。但我不认为这比传递对象本身更好。

【讨论】:

感谢您的回答!至于通过在其中包含一个额外的属性只是为了传递上下文值来传递该有效负载的最初想法,这是一种反模式,因为感觉就像我将有效负载用于不同的目的? 我不认为它是一种反模式。操作负载的目的是为您想要发生的事情提供说明。在简单的情况下,这些指令仅由 reducer 使用,但您也可以为 sagas 或其他自定义中间件提供指令。如果减速器最终没有使用所有指令,那很好。

以上是关于React中的数据传递----上下文(Context)的主要内容,如果未能解决你的问题,请参考以下文章

计算机中的contex理解

React Context上下文学习笔记

魔法? React 上下文提供者从何而来?命名约定?

如何使用 React js 中的上下文将函数从 FUNCTIONAL 传递给 CLASS 组件并在渲染之外(没有 prop)访问它?

React学习笔记 Redux

如何用jquery $.ajax向aspx页面传递json格式数据