언어 유형: Redux는 유한 상태기입니다
이것은 시리즈 문장의 세 번째 편이다.이 직위의 코드는 here
뭐 공부 해요?
사용자가 양식을 제출하면 AJAX 요청이 실행될 때 로드 상태를 표시하고, AJAX 요청이 완료되면 AJAX 요청이 성공하거나 실패할 때 결과를 표시하는 양식을 구축하고자 합니다.
이 작업을 비교하기 위해'고전'감속기와'유한 상태기'감속기를 만듭니다.전체 코드는 this repository 에 있습니다.
클래식 감속기
이것이 바로'고전'감속기의 외관이다.
export default (reduxState: State = defaultState, action: Actions): State => {
switch (action.type) {
case "SUBMIT_FRUIT":
return {
...reduxState,
state: "fruit_loading",
form: action.form
};
case "SUBMIT_FRUIT_ERROR":
return {
...reduxState,
state: "fruit_error",
error: action.error
};
case "SUBMIT_FRUIT_OK":
return {
...reduxState,
state: "fruit_ok",
resonse: action.resonse
};
default:
exhaustiveCheck(action.type);
return reduxState;
}
};
SUBMIT_FRUIT
는 양식 제출에 응답하여 보내는 동작입니다.SUBMIT_FRUIT_ERROR
및SUBMIT_FRUIT_OK
은 AJAX 요청과 같은 부작용에 대응하기 위해 스케줄링됩니다.부작용에 대해 우리는 서로 다른 해결 방안을 사용할 수 있다. 예를 들어reduxthunk,reduxsaga,reduxobservable,reduxloop 등이다.우리는 이 문제에 관심을 갖지 마라. 반대로 우리는 스케줄링을 통해 부작용을 명확하게 촉발할 것이다.다음은 AJAX 요청의 모양입니다.
export const fruitSubmitSideEffect = (dispatch: Dispatch, form: FruitForm) => {
// uses fetch inside returns a Promise
fruitRequest(form).then(
resonse => {
dispatch({
type: "SUBMIT_FRUIT_OK",
resonse
});
},
error => {
dispatch({
type: "SUBMIT_FRUIT_ERROR",
error
});
}
);
};
// and later
export default connect(
() => ({}),
(dispatch: Dispatch) => ({
submit: (form: FruitForm) => {
dispatch({ type: "SUBMIT_FRUIT", form });
fruitSubmitSideEffect(dispatch, form);
}
})
)(Component);
새 상태의 이전 활성 상태를 생성할 수 있지만 명확하게 확인되지 않았습니다.return {
...reduxState,
...newPartsOfState
};
유형 State
은 다음과 같습니다.export type State = {
state: "initial" | "fruit_loading" | "fruit_error" | "fruit_ok";
form?: FruitForm;
error?: mixed;
resonse?: FruitResponse;
};
결과 중 하나는 추가 유형 검사를 작성해야 한다는 것입니다.export default ({ state }: { state: State }) => {
switch (state.state) {
case "fruit_ok":
return (
state.resonse && // additional type check, that it is not undefined
state.resonse.map(item => {}))
}
유한 상태기
유한 상태기(FSM)는 유한 상태를 가정합니다.유형 시스템으로 강제합시다.이것은 흐름 유형이지만 TypeScript는 유사해 보입니다(TS의 경우
{||}
에는 필요하지 않음).export type State =
| {|
state: "initial"
|}
| {|
state: "fruit_loading",
form: FruitForm
|}
| {|
state: "fruit_error",
form: FruitForm,
error: mixed
|}
| {|
state: "fruit_ok",
form: FruitForm,
resonse: FruitResponse
|};
지금 우리는 검사 없이 이전 상태를 사용할 수 없다.하면, 만약, 만약...return {
...reduxState,
state: "fruit_loading",
form: action.form
};
Flow는 다음과 같이 불평합니다.Could not decide which case to select. Since case 2 [1] may work but if it doesn't case 3 [2] looks promising too. To fix add a type annotation to .form [3] or to .state [3].
src/redux-fsm/state.js
[1] 12│ | {|
13│ state: "fruit_loading",
14│ form: FruitForm
15│ |}
[2] 16│ | {|
17│ state: "fruit_error",
18│ form: FruitForm,
19│ error: mixed
20│ |}
그래서 지금 우리는 이렇게 해야 한다.switch (action.type) {
case "SUBMIT_FRUIT":
switch (reduxState.state) {
case "initial":
return {
state: "fruit_loading",
form: action.form
};
default:
throw new Error("Inavlid transition");
}
}
우리는 발생할 동작, 이전의 상태가 무엇인지 검사한 후에 무엇을 할지 결정한다.이런 방법은 우리로 하여금 시스템 중의 모든 전환을 명확하게 고려하게 한다.initial
SUBMIT_FRUIT -> fruit_loading (1)
SUBMIT_FRUIT_ERROR -> ? (2)
SUBMIT_FRUIT_OK -> ? (2)
fruit_loading
SUBMIT_FRUIT -> fruit_loading (3)
SUBMIT_FRUIT_ERROR -> fruit_error (4)
SUBMIT_FRUIT_OK -> fruit_ok (5)
fruit_error
SUBMIT_FRUIT -> fruit_loading (6)
SUBMIT_FRUIT_ERROR -> ? (7)
SUBMIT_FRUIT_OK -> ? (7)
fruit_ok
SUBMIT_FRUIT -> fruit_loading (6)
SUBMIT_FRUIT_ERROR -> ? (7)
SUBMIT_FRUIT_OK -> ? (7)
Side note: Why would you want to do this? To formally specify UIs, to prove that there are no errors in UI logic. For example:
- You can prototype UI logic with sketch.systems
- Use Alloy (lighter alternative to TLA+) to analyze your UI
- This specification can be shared between UX people and developers
- Also, see Verifying ReasonReact component logic — ReasonML & Imandra
Side note 2: I implemented "reversed" FSM in the reducer, it checks action first and the state second
(1,5) "즐거움"경로 - 사용자가 폼을 제출하고 응답을 받습니다.
(1,4) 오류 경로 - 사용자가 양식을 제출하고 오류를 받았습니다.
(6) 반복 검색 - 오류 또는 응답이 발생했고 사용자가 반복 검색합니다.
(2) 일어나지 않는다. - 우리는 그것이 일어나지 않고 이런 상황에서 이상을 일으킨다고 가정할 수 있다.
(7) 경쟁 조건 - 우리는 이미 하나의 응답(또는 오류)과 새로운 응답을 가지고 있으며, 우리가 한 번에 여러 부작용을 허용할 때만 발생할 수 있다.
(3) 중복 검색 - 사용자가 다른 것을 요구하거나 귀찮게 클릭할 수 있는 검색 대기가 있습니다.이것은 재미있는 사례다.우리 어떡하지?우리는 다음과 같이 할 수 있습니다.
이것이 바로 유한상태기를 사용하는 이유이다. 이런 방법은 논리 중의 빈틈을 발견하는 데 도움이 된다.시스템의 상태가 많을수록 숨겨진 잠재적인 구멍이 많아진다.
이러한 유형의 버그를 찾는 것이 너무 번거롭다고 생각되면 일반적인 IT 지원 문제를 생각해 보십시오. "버그를 닫고 다시 열어 보셨습니까?"네, 어떤 곳에서는 상태와 관련된 오류가 숨겨져 있습니다. 해결 방법은 시스템을 다시 시작하여 상태를 초기 상태로 초기화하는 것입니다.
다른 한편, 나는 JS(또는 Flow 또는 TS) 문법이 이런 임무에 있어서 좀 서투르다는 것에 동의한다.스위치의 패턴과 일치하여 표현력이 없습니다.Redux는 기존보다 더 많은 템플릿을 필요로 합니다.너의 생각을 나에게 말해라.만약 그것이 더 적은 템플릿을 필요로 한다면, 당신은 그것을 사용할 수 있습니까?
Photo by Dan Lohmar on Unsplash
. 따라오세요.
Reference
이 문제에 관하여(언어 유형: Redux는 유한 상태기입니다), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/stereobooster/pragmatic-types-how-to-turn-redux-to-finite-state-machine-with-the-help-of-types-5f08텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)