React Native与XState在全局上下文中使用会导致整个应用重新加载。

huangapple go评论57阅读模式
英文:

React Native with XState in Global Context causes entire App to reload

问题

I have a React Native Expo App and I am using XState to keep a global state machine that is passed down the component tree using the Context API.

App.js: (only relevant parts shown - e.g. smSend function here is passed in context.

const GlobalXStateMachineContext = createContext({});

//Here stateDef and actionDef are state machine and actions
const stateMachine = createMachine(stateDef, actionDef);

export default function App() {

console.log("APP INVOKED!!");

const [smState, smSend] = useMachine(stateMachine);
return (

<GlobalXStateMachineContext.Provider value={{ smSend }}>



</GlobalXStateMachineContext.Provider>

);
}
export { GlobalXStateMachineContext };

And then somewhere from deep inside the component tree I am getting a hold of smSend from the context and calling it.

Note that everything is working as expected.
But whenever the smSend() is called from anywhere in the component tree the App() is called leading to a complete reload of the entire tree!

Notice the console.log("APP INVOKED") is called everytime smSend is called like so.

NestedFunction.js (again only relevant code)

import { GlobalXStateMachineContext } from './App';

const NestedFunction = () => {
const smSend = useContext(GlobalXStateMachineContext).smSend;
....
smSend({ payload });
}

I am not sure if this is the expected behavior of Context API or XState or a quirk using the two.
This is resulting in performance degradation and may lead to hard to find bug.

Is there anyway this full reload of the App may be avoided?

英文:

I have a React Native Expo App and I am using XState to keep a global state machine that is passed down the component tree using the Context API.

App.js: (only relevant parts shown - e.g. smSend function here is passed in context.

const GlobalXStateMachineContext = createContext({});
 
//Here stateDef and actionDef are state machine and actions
const stateMachine = createMachine(stateDef, actionDef);

export default function App() {

  console.log(&quot;APP INVOKED!!&quot;);

  const [smState, smSend] = useMachine(stateMachine);
  return (
    &lt;Provider store={store}&gt;
      &lt;GlobalXStateMachineContext.Provider value={{ smSend }}&gt;
        &lt;NavigationContainer&gt;
          &lt;RootScreen /&gt;
        &lt;/NavigationContainer&gt;
      &lt;/GlobalXStateMachineContext.Provider&gt;
    &lt;/Provider&gt;
  );
}
export { GlobalXStateMachineContext };

And then somewhere from deep inside the component tree I am getting a hold of smSend from the context and calling it.

Note that everything is working as expected.
But whenever the smSend() is called from anywhere in the component tree the App() is called leading to a complete reload of the entire tree!

Notice the console.log("APP INVOKED") is called everytime smSend is called like so.

NestedFunction.js (again only relevant code)

import { GlobalXStateMachineContext } from &#39;./App&#39;;

const NestedFunction = () =&gt; {
  const smSend = useContext(GlobalXStateMachineContext).smSend;
  ....
  smSend({ payload });
}

I am not sure if this is the expected behavior of Context API or XState or a quirk using the two.
This is resulting in performance degradation and may lead to hard to find bug.

Is there anyway this full reload of the App may be avoided?

答案1

得分: 0

尝试将像 RootScreen 这样的组件包装在 React.memo 中,这将防止这些组件在不需要时重新渲染。React memo 文档

注意:React.memo 不应该包装那些需要频繁重新渲染的组件。

另外,如果你在 hook 组件内返回内联组件,那些内联组件应该要么移除,要么使用 React.useMemo hook 进行记忆。

英文:

Try wrapping your components such as RootScreen in React.memo this will prevent those components from re-rendering unless needed. React memo docs

FYI React.memo should not wrap components that are meant to re-render a bunch.

Also if you are returning inline components inside a hook component, those inline components should be either removed or memorized with the React.useMemo hook.

答案2

得分: 0

I was able to resolve this issue. This was a documented behavior in XState/react
https://xstate.js.org/docs/recipes/react.html#improving-performance

Instead of the useMachine hook, I switched to useInterpret as described in the docs above.

import { useInterpret } from '@xstate/react';
const scoreService = useInterpret(stateMachine);

...
return (

<GlobalXStateMachineContext.Provider value={{ scoreService }}>

</GlobalXStateMachineContext.Provider>

);
...
export { GlobalXStateMachineContext };

And NestedFunction.js (again only relevant code)

const { send } = useContext(GlobalXStateMachineContext).scoreService;
....
send({ payload });

英文:

I was able to resolve this issue. This was a documented behavior in XState/react
https://xstate.js.org/docs/recipes/react.html#improving-performance

Instead of the useMachine hook, I switched to useInterpret as described in the docs above.

import { useInterpret } from &#39;@xstate/react&#39;;
const scoreService = useInterpret(stateMachine);

...
return (
&lt;Provider store={store}&gt;
  &lt;GlobalXStateMachineContext.Provider value={{ scoreService }}&gt;
    &lt;RootScreen /&gt;
  &lt;/GlobalXStateMachineContext.Provider&gt;
&lt;/Provider&gt;
);
...
export { GlobalXStateMachineContext };

And NestedFunction.js (again only relevant code)

const { send } = useContext(GlobalXStateMachineContext).scoreService;
....
send({ payload });

huangapple
  • 本文由 发表于 2023年7月18日 09:24:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/76708992.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定