如何避免对 redux thunk 使用单独的 _PENDING _FULFILLED 和 _REJECTED 操作?
How do I avoid using separate _PENDING _FULFILLED and _REJECTED actions with redux thunk?
我正在用分派 _PENDING
、_FULFILLED
和 _REJECTED
动作的 thunk 编写我的动作和 reducers。但是,我想要一个更好的解决方案来避免样板文件。我正在迁移到 Typescript,它通过为每个 _PENDING
、_FULFILLED
和 _REJECTED
操作要求一个接口来使这个样板加倍。它正在失控。有没有一种方法可以获得我的代码的 same/similar 功能,而无需每个 thunk 具有三种操作类型?
localUserReducer.js
const initialState = {
fetching: false,
fetched: false,
user: undefined,
errors: undefined,
};
export default function (state = initialState, action) {
switch (action.type) {
case 'GET_USER_PENDING':
return {
...state,
fetching: true,
};
case 'GET_USER_FULFILLED':
return {
...state,
fetching: false,
fetched: true,
user: action.payload,
};
case 'GET_USER_REJECTED':
return {
...state,
fetching: false,
errors: action.payload,
};
default:
return state;
}
}
localUserActions.js
import axios from 'axios';
export const getUser = () => async (dispatch) => {
dispatch({ type: 'GET_USER_PENDING' });
try {
const { data } = await axios.get('/api/auth/local/current');
dispatch({ type: 'GET_USER_FULFILLED', payload: data });
} catch (err) {
dispatch({ type: 'GET_USER_REJECTED', payload: err.response.data });
}
};
新手可能对redux-thunk有很大的误解。如果我使用此处记录的 Typescript 和 redux-thunk 的实现,我不明白如何发送 _REJECTED
操作:https://redux.js.org/recipes/usage-with-typescript#usage-with-redux-thunk
有一种方法可以在每个 thunk 没有三个动作类型的情况下获得类似的功能,但它会对渲染逻辑产生一些影响。
我建议将异步调用的瞬态方面下推到数据。因此,与其将 actions 标记为 _PENDING
、_FULFILLED
和 _REJECTED
,不如将 data 标记为方式,并进行一次操作。
localUser.js(用户类型的新文件)
// Use a discriminated union here to keep inapplicable states isolated
type User =
{ status: 'ABSENT' } |
{ status: 'PENDING' } |
{ status: 'FULLFILLED', data: { fullName: string } } |
{ status: 'REJECTED', error: string };
// a couple of constructors for the fullfilled and rejected data
function dataFulFilled(data: { fullName: string }) {
return ({ status: 'FULLFILLED', data });
}
function dataRejected(error: string) {
return ({ status: 'REJECTED', error });
}
localUserReducer.js
const initialState: { user: User } = { user: { status: 'ABSENT' } };
export default function (state = initialState, action): { user: User } {
switch (action.type) {
case 'USER_CHANGED':
return {
...state,
user: action.payload
};
default:
return state;
}
}
localUserActions.js
import axios from 'axios';
export const getUser = () => async (dispatch) => {
dispatch({ type: 'USER_CHANGED', payload: { status: 'PENDING' } });
try {
const { data } = await axios.get('/api/auth/local/current');
dispatch({ type: 'USER_CHANGED', payload: dataFulFilled(data) });
} catch (err) {
dispatch({ type: 'USER_CHANGED', payload: dataRejected(err.response.data) });
}
};
这也将消除对多个布尔字段(fetching
和 fetched
)的需要,并将各种数据状态与意外修改隔离开来。
渲染逻辑的更改将是必要的,但很可能是一种改进。不是使用布尔值组合嵌套的 if-else 语句,而是可以使用单个开关来处理数据状态的四种情况。
然后你可以从你的渲染函数调用这样的东西...
function userElement(user: User) {
switch (user.status) {
case 'ABSENT':
return <></>;
case 'PENDING':
return <div>Fetching user information...Please be patient...</div>;
case 'FULLFILLED':
return <div>{user.data.fullName}</div>;
case 'REJECTED':
return <h1>The error is: {user.error}</h1>
}
}
希望对您有所帮助。祝你好运!
我正在用分派 _PENDING
、_FULFILLED
和 _REJECTED
动作的 thunk 编写我的动作和 reducers。但是,我想要一个更好的解决方案来避免样板文件。我正在迁移到 Typescript,它通过为每个 _PENDING
、_FULFILLED
和 _REJECTED
操作要求一个接口来使这个样板加倍。它正在失控。有没有一种方法可以获得我的代码的 same/similar 功能,而无需每个 thunk 具有三种操作类型?
localUserReducer.js
const initialState = {
fetching: false,
fetched: false,
user: undefined,
errors: undefined,
};
export default function (state = initialState, action) {
switch (action.type) {
case 'GET_USER_PENDING':
return {
...state,
fetching: true,
};
case 'GET_USER_FULFILLED':
return {
...state,
fetching: false,
fetched: true,
user: action.payload,
};
case 'GET_USER_REJECTED':
return {
...state,
fetching: false,
errors: action.payload,
};
default:
return state;
}
}
localUserActions.js
import axios from 'axios';
export const getUser = () => async (dispatch) => {
dispatch({ type: 'GET_USER_PENDING' });
try {
const { data } = await axios.get('/api/auth/local/current');
dispatch({ type: 'GET_USER_FULFILLED', payload: data });
} catch (err) {
dispatch({ type: 'GET_USER_REJECTED', payload: err.response.data });
}
};
新手可能对redux-thunk有很大的误解。如果我使用此处记录的 Typescript 和 redux-thunk 的实现,我不明白如何发送 _REJECTED
操作:https://redux.js.org/recipes/usage-with-typescript#usage-with-redux-thunk
有一种方法可以在每个 thunk 没有三个动作类型的情况下获得类似的功能,但它会对渲染逻辑产生一些影响。
我建议将异步调用的瞬态方面下推到数据。因此,与其将 actions 标记为 _PENDING
、_FULFILLED
和 _REJECTED
,不如将 data 标记为方式,并进行一次操作。
localUser.js(用户类型的新文件)
// Use a discriminated union here to keep inapplicable states isolated
type User =
{ status: 'ABSENT' } |
{ status: 'PENDING' } |
{ status: 'FULLFILLED', data: { fullName: string } } |
{ status: 'REJECTED', error: string };
// a couple of constructors for the fullfilled and rejected data
function dataFulFilled(data: { fullName: string }) {
return ({ status: 'FULLFILLED', data });
}
function dataRejected(error: string) {
return ({ status: 'REJECTED', error });
}
localUserReducer.js
const initialState: { user: User } = { user: { status: 'ABSENT' } };
export default function (state = initialState, action): { user: User } {
switch (action.type) {
case 'USER_CHANGED':
return {
...state,
user: action.payload
};
default:
return state;
}
}
localUserActions.js
import axios from 'axios';
export const getUser = () => async (dispatch) => {
dispatch({ type: 'USER_CHANGED', payload: { status: 'PENDING' } });
try {
const { data } = await axios.get('/api/auth/local/current');
dispatch({ type: 'USER_CHANGED', payload: dataFulFilled(data) });
} catch (err) {
dispatch({ type: 'USER_CHANGED', payload: dataRejected(err.response.data) });
}
};
这也将消除对多个布尔字段(fetching
和 fetched
)的需要,并将各种数据状态与意外修改隔离开来。
渲染逻辑的更改将是必要的,但很可能是一种改进。不是使用布尔值组合嵌套的 if-else 语句,而是可以使用单个开关来处理数据状态的四种情况。
然后你可以从你的渲染函数调用这样的东西...
function userElement(user: User) {
switch (user.status) {
case 'ABSENT':
return <></>;
case 'PENDING':
return <div>Fetching user information...Please be patient...</div>;
case 'FULLFILLED':
return <div>{user.data.fullName}</div>;
case 'REJECTED':
return <h1>The error is: {user.error}</h1>
}
}
希望对您有所帮助。祝你好运!