减少 redux-thunk 样板文件
Reducing redux-thunk boilerplate
在编写 redux-thunk
函数(称为 thunks 时,有很多样板文件可以很容易地抽象掉。例如,在我们的大多数异步 API 调用中,我们正在执行以下操作,没有任何副作用:
export const LOGIN_REQUEST = 'my-app/auth/LOGIN_REQUEST';
export const LOGIN_RECIEVE = 'my-app/auth/LOGIN_RECIEVE';
export const LOGIN_FAILURE = 'my-app/auth/LOGIN_FAILURE';
// ... reducer code here
export function login(loginHandle, password) {
return (dispatch, getState, api) => {
dispatch({ type: LOGIN_REQUEST });
api.post('/auth/login', { loginHandle, password }).then(
response => dispatch({ type: LOGIN_RECIEVE, response }),
error => dispatch({ type: LOGIN_FAILURE, error })
);
};
}
简单!虽然这至少涵盖了我们 70% 的请求,但我确信有一种优雅的方法可以将上述代码的分配抽象为类似这样的东西(伪代码):
export function login(loginHandle, password) {
return (dispatch, getState, api) => api('POST', LOGIN_REQUEST, '/auth/login', { loginHandle, password });
}
当我们需要检查状态和其他副作用时,我们可以返回到适当的 thunk。尽管对于大多数情况...我们可以减少它?
有什么高雅的点子吗?
自 2.1.0 起,Redux Thunk 允许您注入自定义参数。
const api = createApi() // you would write this function
const store = createStore(
reducer,
applyMiddleware(thunk.withExtraArgument(api))
)
// your action creator:
function fetchUser(id) {
return (dispatch, getState, api) => {
// you can use api here
}
}
将来,如果您的 thunk 变得太复杂,您可能需要考虑 or redux-observable。
不幸的是,redux 社区没有解决这个问题的通用方法。我个人认为人们不应该害怕围绕 redux 编写自己的自定义包装器来处理这种情况。
我创建了一个名为 redux-tiles 的库,它实际上几乎完全符合您的需要 API :) 例如,您的代码将如下所示:
import { createTile } from 'redux-tiles';
const login = createTile({
type: ['user', 'login'],
// params is an argument with which you call an action
fn: ({ api, params }) => api('POST', '/auth/login', params),
});
如你所见,这里没有常量,也没有reducer。这些东西是自动创建的,所以你不必去做,也不必测试它。还有其他功能,例如嵌套(因此相同的功能将应用于通过 id 获取项目,例如,但它们将在 reducer 内正确更新)和缓存。您可以查看示例 here.
Redux-saga 也是一个好东西,但如果你需要某种反应性,它就更好了,如果你需要更传统的方法,只是更方便地描述你的动作并将它们组合起来,而不是重复你自己,然后我发现我的图书馆是一个完美的匹配。
在编写 redux-thunk
函数(称为 thunks 时,有很多样板文件可以很容易地抽象掉。例如,在我们的大多数异步 API 调用中,我们正在执行以下操作,没有任何副作用:
export const LOGIN_REQUEST = 'my-app/auth/LOGIN_REQUEST';
export const LOGIN_RECIEVE = 'my-app/auth/LOGIN_RECIEVE';
export const LOGIN_FAILURE = 'my-app/auth/LOGIN_FAILURE';
// ... reducer code here
export function login(loginHandle, password) {
return (dispatch, getState, api) => {
dispatch({ type: LOGIN_REQUEST });
api.post('/auth/login', { loginHandle, password }).then(
response => dispatch({ type: LOGIN_RECIEVE, response }),
error => dispatch({ type: LOGIN_FAILURE, error })
);
};
}
简单!虽然这至少涵盖了我们 70% 的请求,但我确信有一种优雅的方法可以将上述代码的分配抽象为类似这样的东西(伪代码):
export function login(loginHandle, password) {
return (dispatch, getState, api) => api('POST', LOGIN_REQUEST, '/auth/login', { loginHandle, password });
}
当我们需要检查状态和其他副作用时,我们可以返回到适当的 thunk。尽管对于大多数情况...我们可以减少它?
有什么高雅的点子吗?
自 2.1.0 起,Redux Thunk 允许您注入自定义参数。
const api = createApi() // you would write this function
const store = createStore(
reducer,
applyMiddleware(thunk.withExtraArgument(api))
)
// your action creator:
function fetchUser(id) {
return (dispatch, getState, api) => {
// you can use api here
}
}
将来,如果您的 thunk 变得太复杂,您可能需要考虑
不幸的是,redux 社区没有解决这个问题的通用方法。我个人认为人们不应该害怕围绕 redux 编写自己的自定义包装器来处理这种情况。
我创建了一个名为 redux-tiles 的库,它实际上几乎完全符合您的需要 API :) 例如,您的代码将如下所示:
import { createTile } from 'redux-tiles';
const login = createTile({
type: ['user', 'login'],
// params is an argument with which you call an action
fn: ({ api, params }) => api('POST', '/auth/login', params),
});
如你所见,这里没有常量,也没有reducer。这些东西是自动创建的,所以你不必去做,也不必测试它。还有其他功能,例如嵌套(因此相同的功能将应用于通过 id 获取项目,例如,但它们将在 reducer 内正确更新)和缓存。您可以查看示例 here.
Redux-saga 也是一个好东西,但如果你需要某种反应性,它就更好了,如果你需要更传统的方法,只是更方便地描述你的动作并将它们组合起来,而不是重复你自己,然后我发现我的图书馆是一个完美的匹配。