【问题标题】:Retain callback between rerenders在重新渲染之间保留回调
【发布时间】:2021-05-19 04:01:59
【问题描述】:

我正在构建一个带有受保护操作的应用,并希望实现以下流程:

  • 匿名用户尝试运行受保护的操作(例如播放视频 - 实质上是修改 React 状态)
  • 登录模式弹出,没有任何重定向
  • 用户填写凭据并点击登录按钮
  • 只要用户登录,任何类型的加载器都会显示(并替换之前的应用树)并停留在屏幕上
  • 一旦登录,加载程序就会消失,并且经过身份验证的应用程序会呈现
  • 视频开始播放,因为应用知道用户想要执行的操作

我还没有找到一种方法来做到这一点,因为用加载器替换旧的应用程序树,然后将其放回屏幕上会使操作在一个已经取消的组件上运行。

我正在使用一个 <UserProvider> 组件来包装整个树,为它提供authenticated 状态。

Codesandbox 来说明问题:

https://codesandbox.io/s/react-context-callback-l9xe9?file=/src/App.js

const MyContext = React.createContext();

const MyProvider = ({ children }) => {
  const [auth, setAuth] = React.useState(false);
  const [reset, setReset] = React.useState(false);
  const [callback, setCallback] = React.useState(null);
  const guard = (cb) => {
    auth ? cb() : setCallback(() => cb);
  };

  React.useEffect(() => {
    if (auth) {
      setReset(true);
      setTimeout(() => {
        setReset(false);
      }, 1000);
    }
  }, [auth]);

  if (reset) {
    return <p style={{ color: "red" }}>I pretend I'm logging you in</p>;
  }

  return (
    <MyContext.Provider
      value={{
        setAuth,
        auth,
        callback,
        setCallback,
        guard
      }}
    >
      {children}
    </MyContext.Provider>
  );
};

const Child = () => {
  const [count, setCount] = React.useState(0);
  const mounted = React.useRef(false);

  React.useEffect(() => {
    mounted.current = true;

    return () => {
      mounted.current = false;
    };
  }, []);
  const myCallback = () => {
    setCount((cnt) => cnt + 1);
  };
  const { auth, setAuth, guard, callback, setCallback } = React.useContext(
    MyContext
  );

  return (
    <div>
      logged in: {`${auth}`}
      <br />
      count: {count}
      <br />
      {callback && (
        <button
          onClick={() => {
            setAuth(true);
            callback();
            setCallback(null);
          }}
        >
          pretend login
        </button>
      )}
      <button onClick={() => guard(myCallback)}>
        increment (when authenticated)
      </button>
    </div>
  );
};

这可能吗?

【问题讨论】:

  • 以目前的逻辑,你不能,你必须提升状态。

标签: reactjs


【解决方案1】:

这是lifting the state up 的一个用例,您无法在组件卸载后保留状态(当然您可以尝试从本地存储中读取,但这与解除状态的逻辑基本相同)。

因此解除count 状态(到App 或提供者本身):

export default function App() {
  const state = React.useState(0);
  ...

【讨论】:

  • 感谢您的想法,试一试,它可以工作 - codesandbox.io/s/react-context-callback-forked-fivtc?file=/src/…。但是,这适用于这个非常简单的用例。我无法想象对我需要在真实应用程序中保护的每一个动作都执行此操作 - 我认为这会带来道具钻孔等问题。想象一下,您需要在应用程序内使用不同级别的嵌套来保护不同组件中的不同动作。如何实现它?
  • 提供者可以为所有身份验证组件保持状态(不推荐),它知道它们是否已安装。可以相应地初始化组件本身。但是,您已经传递了 auth 布尔值,组件可以像在这种情况下一样初始化自身(必须从 count=1 开始)。
  • 您知道组件没有真正受到保护?在现实生活场景中,您应该使用受保护的路由。
  • 我们需要保护路线和行动。匿名用户可能会看到某些操作受限的路线(例如您在登录之前无法播放视频,但您可以看到电影详细信息页面)。一旦你想开始玩,你就面临登录模式。
  • 关于前面的评论,我可以用某种onAuthenticatedMountCallback 属性初始化受保护的组件,这样就可以了。因为我只渲染&lt;Provider&gt;{children}&lt;/Provider&gt;,所以我必须对组件类型进行一些检查,但这可以工作,对吧?
猜你喜欢
  • 2020-12-10
  • 1970-01-01
  • 2022-09-30
  • 2012-11-11
  • 2014-09-22
  • 1970-01-01
  • 2013-04-07
  • 1970-01-01
  • 2015-06-04
相关资源
最近更新 更多