如何使用 Recoil 在 React 组件之外操作全局状态?

问题描述

我正在使用 Recoil,我想从实用程序函数内部访问组件外部的存储(获取/设置)。

更一般地说,人们如何编写可重用的函数来操纵 Recoil 的全局状态?使用 Redux,我们可以直接将事件分派到 store,但我还没有找到 Recoil 的替代方案。

使用钩子是一种很棒的开发者体验,但很难将组件内定义的函数转换为外部实用程序函数,因为钩子只能在组件内使用。

解决方法

我设法调整了 https://github.com/facebookexperimental/Recoil/issues/289#issuecomment-777249693 答案并使其适用于 Next.js 框架。 (见下面的使用示例)

此解决方法允许将 Recoil Root 用作一种全局状态。不过,它只有在只有一个 RecoilRoot 组件时才有效。

// RecoilExternalStatePortal.tsx
import {
  Loadable,RecoilState,RecoilValue,useRecoilCallback,useRecoilTransactionObserver_UNSTABLE,} from 'recoil';

/**
 * Returns a Recoil state value,from anywhere in the app.
 *
 * Can be used outside of the React tree (outside a React component),such as in utility scripts,etc.

 * <RecoilExternalStatePortal> must have been previously loaded in the React tree,or it won't work.
 * Initialized as a dummy function "() => null",it's reference is updated to a proper Recoil state mutator when RecoilExternalStatePortal is loaded.
 *
 * @example const lastCreatedUser = getRecoilExternalLoadable(lastCreatedUserState);
 */
export let getRecoilExternalLoadable: <T>(
  recoilValue: RecoilValue<T>,) => Loadable<T> = () => null as any;

/**
 * Sets a Recoil state value,etc.
 *
 * <RecoilExternalStatePortal> must have been previously loaded in the React tree,it's reference is updated to a proper Recoil state mutator when RecoilExternalStatePortal is loaded.
 *
 * @example setRecoilExternalState(lastCreatedUserState,newUser)
 */
export let setRecoilExternalState: <T>(
  recoilState: RecoilState<T>,valOrUpdater: ((currVal: T) => T) | T,) => void = () => null as any;

/**
 * Utility component allowing to use the Recoil state outside of a React component.
 *
 * It must be loaded in the _app file,inside the <RecoilRoot> component.
 * Once it's been loaded in the React tree,it allows using setRecoilExternalState and getRecoilExternalLoadable from anywhere in the app.
 *
 * @see https://github.com/facebookexperimental/Recoil/issues/289#issuecomment-777300212
 * @see https://github.com/facebookexperimental/Recoil/issues/289#issuecomment-777305884
 * @see https://recoiljs.org/docs/api-reference/core/Loadable/
 */
export function RecoilExternalStatePortal() {
  // We need to update the getRecoilExternalLoadable every time there's a new snapshot
  // Otherwise we will load old values from when the component was mounted
  useRecoilTransactionObserver_UNSTABLE(({ snapshot }) => {
    getRecoilExternalLoadable = snapshot.getLoadable;
  });

  // We only need to assign setRecoilExternalState once because it's not temporally dependent like "get" is
  useRecoilCallback(({ set }) => {
    setRecoilExternalState = set;

    return async () => {

    };
  })();

  return <></>;
}

使用 Next.js 框架的配置示例:

// pages/_app.tsx

import {
  NextComponentType,NextPageContext,} from 'next';
import { Router } from 'next/router';
import React from 'react';
import { RecoilRoot } from 'recoil';
import { RecoilExternalStatePortal } from '../components/RecoilExternalStatePortal';

type Props = {
  Component: NextComponentType<NextPageContext>; // Page component,not provided if pageProps.statusCode is 3xx or 4xx
  err?: Error; // Only defined if there was an error
  pageProps: any; // Props forwarded to the Page component
  router?: Router; // Next.js router state
};

/**
 * This file is the entry point for all pages,it initialize all pages.
 *
 * It can be executed server side or browser side.
 *
 * @see https://nextjs.org/docs/advanced-features/custom-app Custom _app
 * @see https://nextjs.org/docs/basic-features/typescript#custom-app TypeScript for _app
 */
const App: React.FunctionComponent<Props> = (props): JSX.Element => {
  const { Component,pageProps} = props;

  return (
      <RecoilRoot>
        <Component {...pageProps} />
        <RecoilExternalStatePortal />
      </RecoilRoot>
  );
};
// Anywhere,e.g: src/utils/user.ts

const createUser = (newUser) => {
  setRecoilExternalState(lastCreatedUserState,newUser)
}

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...