Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
302 views
in Technique[技术] by (71.8m points)

reactjs - Combine useMachine with useContext

I'm working on a UI project which handles state updates through a shared context, very similar as described here

const {appState, dispatch} = useContext(AppContext);

I'm not toying around with state machines through xstate for some of the components.

const SomeComponent = () => {
  const {appState, dispatch} = useContext(AppContext);
  const [state,send,service] = useMachine(MyComponentStateMachine);
}

Now, ideally I would like my state machine to dispatch certain events when entering a state. What's the best way for the state machine to get a hold of my AppContext, though?

At the moment, I'm handling this event dispatching on the component itself, observing the state of the state machine and dispatching an event as needed when it enters a certain state:

const SomeComponent = () => {
  const {appState, dispatch} = useContext(AppContext);
  const [state,send,service] = useMachine(MyComponentStateMachine);

  useEffect(() => service.subscribe(state => {
     if(state.value == "Some relevant state of MyComponentStateMachine")
       dispatch({type: SomeEvent, arg: 12345});  
    }).unsubscribe, [service]);
}

This works well, but it strikes me as bad design. I'd think it would be cleaner to dispatch this event from the state machine directly, rather than from the component.

Is there any good way for the state machine to get a hold of AppContext?

Would it be sensible to simply create a factory method for the state machine which takes dispatch as an argument, and holds on to it?

question from:https://stackoverflow.com/questions/65831389/combine-usemachine-with-usecontext

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I believe there's nothing wrong to call your dispatch function there. Due that you are using context, you wouldn't be able to call the dispatch inside the machine, unless you pass the function as a parameter. You can try that, not sure it that would work.

(You can use actions to trigger side effects on events or state changes)

In that case it would be something like this:

<pre>
  //Component
  const SomeComponent = () => {
    const { appState, dispatch } = useContext(AppContext);
    const [ state, send ] = useMachine(MyComponentStateMachine);

    useEffect(() => {
      if(state.matches("stateName")) {
        const data = { type: SomeEvent, arg: 12345 };
        send("EVENT_NAME", { callback: dispatch, data })
      }
    }, [state]);
  }


  //Machine
  const MyComponentStateMachine = Machine({
    ...
    states: {
      stateName: {
        on: {
          EVENT_NAME: "secondState",
        },
      },
      secondState: {
        entry: ["actionName"]
      }
    },
    {
      actions: {
        actionName: (ctx, e) => {
          e.callback(e.data);
        },
      }
    }
  });
</pre>

*** Also look how I compare the state, that is a cleaner way to read the state value. Also you don't need to subscribe to the service if you are already using the useMachine hook, the hook will trigger a component rerender if the state changes.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...