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
3.3k views
in Technique[技术] by (71.8m points)

javascript - Do React state updates occur in order when used like an FSM and useEffect?

I am trying to build a simple FSM (finite state machine) in ReactJS functional components. I am looking at this which specifies that they it can do it asynchronously and second they may merge the state changes.

Basically I am wondering because of the behaviour specified is it possible to create an FSM? I am trying to trim the example as small as I can

I am making a few restrictions on my usage of the setter, namely it will only be executed in:

  • a state handler function
  • an event handler (and even this is limitted)
  • a state can have its own substate which is another state store (basically additional parameters that may only be read or written by the two above functions)

Anyway I set my initial state and I have a single useEffect hook for the state as follows

const [fsmState, setFsmState] = useState(States.Initial)
useEffect(() => {
  stateHandlers[fsmState]();
}, [fsmState]);

States is an enum

enum States {
  Initial,
  Unauthenticated,
  Authenticated,
  Processing
}

I define stateHandlers as follows:

  const stateHandlers: {
    [key in States]: (...params: any) => void | Promise<void>;
  } = {
    [States.Initial]: async () => { 
      // this may load authentication state from a store, 
      // for now just set it to Unauthenticated
      setFsmState(State.Unauthenticated);
    },
    [States.SigningIn]: async () => { 
      const authToken = await loginToServer(signInState));
      if (authToken.isValid()) {
        setFsmState(State.Authenticated);
      } else {
        setFsmState(State.Unauthenticated);
      }
    },
    [States.Authenticated]: () => { },
    [States.Unauthenticated]: () => { },
  }

For the event handler e.g. sign in I do this (which is basically the reason I am asking the question.

const signinAsync = async (username: string, password: string) => {
  if (fsmState !== States.Unauthenticated) throw Error("invalid state");
  // would these two get set in order?
  setSignInState({username,password})
  setFsmState(State.SigningIn);
}

I just wanted a simple TSX implementation to avoid adding additional libraries. However, I am still in the midst of coding this as I learn TypeScript/React.


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

1 Reply

0 votes
by (71.8m points)

Yes it is possible. In fact,there is a well known FMS library called xstate https://github.com/davidkpiano/xstate.

Updating state will always be asynchronous because that is how it works and rightly so. In your state machine handler, you just need to make sure that the transition is correct. I can already see that your implementation is already heading to the right direction.

const signinAsync = async (username: string, password: string) => {
  if (fsmState !== States.Unauthenticated) throw Error("invalid state");
  // would these two get set in order?
  setSignInState({username,password})
  setFsmState(State.SigningIn);
}

There is no guarantee which one will happen first. Your implementation should handle all cases, whether setSignInState or setFsmState happens first. Both actions should be independent. If you need both to be in a series then you need to chain them in a callback or something. For example, with setState you can pass a second arguments as a callback that will be called after state is updated.

this.setState({ title: event.target.value }, function() {
    this.validateTitle(); // thid will happen after state update
  });

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

...