英文:
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("APP INVOKED!!");
const [smState, smSend] = useMachine(stateMachine);
return (
<Provider store={store}>
<GlobalXStateMachineContext.Provider value={{ smSend }}>
<NavigationContainer>
<RootScreen />
</NavigationContainer>
</GlobalXStateMachineContext.Provider>
</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?
答案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 '@xstate/react';
const scoreService = useInterpret(stateMachine);
...
return (
<Provider store={store}>
<GlobalXStateMachineContext.Provider value={{ scoreService }}>
<RootScreen />
</GlobalXStateMachineContext.Provider>
</Provider>
);
...
export { GlobalXStateMachineContext };
And NestedFunction.js (again only relevant code)
const { send } = useContext(GlobalXStateMachineContext).scoreService;
....
send({ payload });
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论