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

How to pass a value from React Context into a Redux-Saga function?

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

// 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 函数中,以便它可以使用它?

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

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


但是您使用的是上下文,所以它可能是一个确实发生变化的值。所以这将我们带到下一个案例:只有一个副本,它的值 does 改变,但只有组件关心它何时改变(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 开始,检查值,如果它发现值是假的,它需要休眠直到值变为真。

为了支持这一点,您将需要具有传奇可以订阅的某种事件发射器。这种代码有很多现有的库,我们可以推出自己的库,例如:

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 无法知道使用哪一个。没办法,当然只能告诉它,你会通过动作负载来做,然后让我们回到最初的想法。

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