Tìm kiếm trang web

javascript - Các cập nhật state trong React có xảy ra theo thứ tự khi được sử dụng như một FSM và useEffect không?


Tôi đang cố gắng xây dựng một FSM (máy trạng thái hữu hạn) đơn giản trong các thành phần chức năng ReactJS. Tôi đang xem xét cái này, nơi chỉ ra rằng nó có thể thực hiện một cách bất đồng bộ và thứ hai là họ có thể hợp nhất các thay đổi trạng thái.

Trong các thành phần lớp, bạn có thể liên kết các thay đổi trạng thái bằng cách sử dụng phương thức setState() với hai đối số. Nhưng khả năng đó không có sẵn trong hook useState().

Về cơ bản tôi đang tự hỏi vì hành vi đã nêu có thể tạo ra một FSM không? Tôi đang cố gắng thu gọn ví dụ này nhỏ nhất có thể

Tôi đang đặt một vài hạn chế về việc sử dụng setter của mình, cụ thể là nó chỉ được thực hiện trong:

  • một hàm xử lý trạng thái
  • một trình xử lý sự kiện (và ngay cả điều này cũng bị hạn chế)
  • một bang có thể có tiểu bang riêng của nó là một kho bang khác (về cơ bản là các tham số bổ sung chỉ có thể được đọc hoặc ghi bởi hai hàm trên)

Dù sao tôi đã đặt trạng thái ban đầu của mình và tôi có một hook useEffect duy nhất cho trạng thái như sau

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

States là một enum

enum States {
  Initial,
  Unauthenticated,
  Authenticated,
  Processing
}

Tôi định nghĩa stateHandlers như sau:

  const stateHandlers: {
    [key in States]: () => 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]: () => { },
  }

Đối với trình xử lý sự kiện ví dụ như đăng nhập, tôi làm như sau (điều này cơ bản là lý do tôi đang hỏi câu hỏi này).

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);
}

Như đã nêu trước đó, phiên bản hai đối số không có sẵn nên tôi không thể thực hiện

setSignInState({username,password},
  () => { setFsmState(State.SigningIn) } )

Tôi chỉ muốn một triển khai TSX đơn giản để tránh thêm các thư viện khác. Tuy nhiên, tôi vẫn đang trong quá trình lập trình điều này khi học TypeScript/React.

Câu trả lời #1

Vâng, điều đó là có thể. Thực ra, có một thư viện FMS nổi tiếng gọi là xstate https://github.com/davidkpiano/xstate.

Cập nhật trạng thái sẽ luôn bất đồng bộ bởi vì đó là cách nó hoạt động và điều đó là đúng. Trong trình xử lý máy trạng thái của bạn, bạn chỉ cần đảm bảo rằng việc chuyển đổi là chính xác. Tôi đã thấy rằng triển khai của bạn đang đi đúng hướng.

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);
}

Không có gì đảm bảo cái nào sẽ xảy ra trước. Việc triển khai của bạn nên xử lý tất cả các trường hợp, dù setSignInState hay setFsmState xảy ra trước. Cả hai hành động nên độc lập. Nếu bạn cần cả hai thực hiện theo một chuỗi thì bạn cần nối chúng trong một callback hoặc cái gì đó tương tự. Ví dụ, với setState bạn có thể truyền đối số thứ hai là một callback sẽ được gọi sau khi state được cập nhật.

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

Bài viết liên quan