Redux saga 和 redux 工具包
Redux saga & redux toolkit
我一直在尝试将 redux sagas 和 redux 工具包引入我的项目。我现在遇到的问题是观察者传奇没有捕捉到 takeEvery
效果和 运行 处理程序中的调度操作。我看不出代码有什么问题。谁能帮忙!!!
import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit'
import createSagaMiddleware from 'redux-saga'
import logger from 'redux-logger';
import createReducer from './rootReducer';
import sagas from './rootSaga';
const configureAdminStore = (initialState = {}) => {
const sagaMiddleware = createSagaMiddleware();
// sagaMiddleware: Makes redux-sagas work
const middlewares = [sagaMiddleware, logger];
const store = configureStore({
reducer: createReducer(),
middleware: [...getDefaultMiddleware({thunk: false}), ...middlewares],
preloadedState: initialState,
devTools: process.env.NODE_ENV !== 'production',
});
sagaMiddleware.run(sagas);
return store;
}
export default configureAdminStore;
import {put, take, takeEvery, call} from 'redux-saga/effects'
import {getAll} from './environmentSlice'
import {confApi} from '../../service/conf-api'
import { getData } from '../../lib/conf-api-response';
function* getAllEnvironments() {
const response = yield call(confApi.admin.getEnvironments());
const {environments} = yield call(getData(response));
yield put(getAll(environments));
}
// eslint-disable-next-line import/prefer-default-export
export function* watchGetAllEnvironments() {
yield takeEvery(getAll().type, getAllEnvironments);
}
import { createSlice } from '@reduxjs/toolkit'
const environmentSlice = createSlice({
name: 'environments',
initialState: [],
reducers: {
getAll: (state, action) => {
state = action.payload
},
},
})
export const {getAll} = environmentSlice.actions
export const { getAllSuccess } = environmentSlice.actions;
export default environmentSlice.reducer
export const environmentSelector = (state) => state.environments
import {all} from 'redux-saga/effects'
import {watchGetAllEnvironments} from './environments/environmentSaga'
export default function* rootSaga() {
yield all([
watchGetAllEnvironments(),
])
}
看起来您只服用了两次 getAll().type
- 一次在 watchGetAllEnvironments
中,一次在 getAllEnvironments
中。
这意味着 watchGetAllEnvironments
将执行 getAllEnvironments
,但会立即暂停并等待另一个 getAll
动作被调度,这可能永远不会发生。
所以您可能想删除 getAllEnvironments
中的第一个 take
。
另外,take(getAll)
即可,不需要take(getAll().type)
。
官方文档可能会有帮助,
import { applyMiddleware, createStore } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension'
import thunkMiddleware from 'redux-thunk'
import monitorReducersEnhancer from './enhancers/monitorReducers'
import loggerMiddleware from './middleware/logger'
import rootReducer from './reducers'
export default function configureStore(preloadedState) {
const middlewares = [loggerMiddleware, thunkMiddleware]
const middlewareEnhancer = applyMiddleware(...middlewares)
const enhancers = [middlewareEnhancer, monitorReducersEnhancer]
const composedEnhancers = composeWithDevTools(...enhancers)
const store = createStore(rootReducer, preloadedState, composedEnhancers)
if (process.env.NODE_ENV !== 'production' && module.hot) {
module.hot.accept('./reducers', () => store.replaceReducer(rootReducer))
}
return store
}
如果您有兴趣创建可以 resolve/reject 异步 thunk 操作的传奇,那么请查看 saga-toolkit 包 - 我创建并使用了。
slice.js
import { createSlice } from '@reduxjs/toolkit'
import { createSagaAction } from 'saga-toolkit'
const name = 'example'
const initialState = {
result: null,
loading: false,
error: null,
}
export const fetchThings = createSagaAction(`${name}/fetchThings`)
export const doSomeMoreAsyncStuff = createSagaAction(`${name}/doSomeMoreAsyncStuff`)
const slice = createSlice({
name,
initialState,
extraReducers: {
[fetchThings.pending]: () => ({
loading: true,
}),
[fetchThings.fulfilled]: ({ payload }) => ({
result: payload,
loading: false,
}),
[fetchThings.rejected]: ({ error }) => ({
error,
loading: false,
}),
},
})
export default slice.reducer
sagas.js
import { call } from 'redux-saga/effects'
import { takeLatestAsync, takeEveryAsync, putAsync } from 'saga-toolkit'
import API from 'hyper-super-api'
import * as actions from './slice'
function* fetchThings() {
const result = yield call(() => API.get('/things'))
const anotherResult = yield putAsync(actions.doSomeMoreAsyncStuff()) // waits for doSomeMoreAsyncStuff to finish !
return result
}
function* doSomeMoreAsyncStuff() {
...
return 'a value for another result'
}
export default [
takeLatestAsync(actions.fetchThings.pending, fetchThings), // takeLatestAsync: behaves a bit like debounce
takeEveryAsync(actions.doSomeMoreAsyncStuff.pending, doSomeMoreAsyncStuff), // each action will start a new saga thread
]
如果这对任何新来者有帮助,我发现 redux-toolkit 很酷但仍然有点复杂,尤其是当你加入 sagas 时(一开始它本质上是异步魔法和神秘)。我最终做出了类似的东西,我称之为 saga slice。它是 redux 工具包的衍生产品,但从一开始就添加了 sagas。你基本上有你的 reducers,它操纵状态,你的 sagas,它使异步调用和调度其他状态。为了简化您设置的示例,它最终看起来像这样:
import { put } from "redux-saga/effects";
import { createModule } from 'saga-slice';
const environmentSlice = createModule({
name: 'environments',
initialState: [],
reducers: {
getAll: () => {},
getAllSuccess: (state, payload) => {
state.splice(0, state.length);
payload.forEach(data => state.push(data));
},
},
sagas: (A) => ({
*[A.getAll]({ payload }) {
const response = yield call(confApi.admin.getEnvironments());
const { environments } = yield call(getData(response));
yield put(A.getAllSuccess(environments));
}
})
});
这基本上会做您正在尝试做的事情,而没有令人困惑的 watchGetAllEnvironments
和 rootSaga
东西(我觉得这很令人费解)。 Saga 切片可配置为使用 takeEvery
、takeOne
或您想要使用的任何其他效果,而不会太麻烦。它是配置对象,而不是 yield takeEvery()
。我还包含帮助程序,通过包含一个 axios
包装器来简化您正在做的一些事情,该包装器与 saga 的任务取消、流线请求生命周期以及基于标准 REST 原则构建默认 CRUD 模块相关联。查看文档以获得更详尽的解释,并随时打开问题!
我一直在尝试将 redux sagas 和 redux 工具包引入我的项目。我现在遇到的问题是观察者传奇没有捕捉到 takeEvery
效果和 运行 处理程序中的调度操作。我看不出代码有什么问题。谁能帮忙!!!
import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit'
import createSagaMiddleware from 'redux-saga'
import logger from 'redux-logger';
import createReducer from './rootReducer';
import sagas from './rootSaga';
const configureAdminStore = (initialState = {}) => {
const sagaMiddleware = createSagaMiddleware();
// sagaMiddleware: Makes redux-sagas work
const middlewares = [sagaMiddleware, logger];
const store = configureStore({
reducer: createReducer(),
middleware: [...getDefaultMiddleware({thunk: false}), ...middlewares],
preloadedState: initialState,
devTools: process.env.NODE_ENV !== 'production',
});
sagaMiddleware.run(sagas);
return store;
}
export default configureAdminStore;
import {put, take, takeEvery, call} from 'redux-saga/effects'
import {getAll} from './environmentSlice'
import {confApi} from '../../service/conf-api'
import { getData } from '../../lib/conf-api-response';
function* getAllEnvironments() {
const response = yield call(confApi.admin.getEnvironments());
const {environments} = yield call(getData(response));
yield put(getAll(environments));
}
// eslint-disable-next-line import/prefer-default-export
export function* watchGetAllEnvironments() {
yield takeEvery(getAll().type, getAllEnvironments);
}
import { createSlice } from '@reduxjs/toolkit'
const environmentSlice = createSlice({
name: 'environments',
initialState: [],
reducers: {
getAll: (state, action) => {
state = action.payload
},
},
})
export const {getAll} = environmentSlice.actions
export const { getAllSuccess } = environmentSlice.actions;
export default environmentSlice.reducer
export const environmentSelector = (state) => state.environments
import {all} from 'redux-saga/effects'
import {watchGetAllEnvironments} from './environments/environmentSaga'
export default function* rootSaga() {
yield all([
watchGetAllEnvironments(),
])
}
看起来您只服用了两次 getAll().type
- 一次在 watchGetAllEnvironments
中,一次在 getAllEnvironments
中。
这意味着 watchGetAllEnvironments
将执行 getAllEnvironments
,但会立即暂停并等待另一个 getAll
动作被调度,这可能永远不会发生。
所以您可能想删除 getAllEnvironments
中的第一个 take
。
另外,take(getAll)
即可,不需要take(getAll().type)
。
官方文档可能会有帮助,
import { applyMiddleware, createStore } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension'
import thunkMiddleware from 'redux-thunk'
import monitorReducersEnhancer from './enhancers/monitorReducers'
import loggerMiddleware from './middleware/logger'
import rootReducer from './reducers'
export default function configureStore(preloadedState) {
const middlewares = [loggerMiddleware, thunkMiddleware]
const middlewareEnhancer = applyMiddleware(...middlewares)
const enhancers = [middlewareEnhancer, monitorReducersEnhancer]
const composedEnhancers = composeWithDevTools(...enhancers)
const store = createStore(rootReducer, preloadedState, composedEnhancers)
if (process.env.NODE_ENV !== 'production' && module.hot) {
module.hot.accept('./reducers', () => store.replaceReducer(rootReducer))
}
return store
}
如果您有兴趣创建可以 resolve/reject 异步 thunk 操作的传奇,那么请查看 saga-toolkit 包 - 我创建并使用了。
slice.js
import { createSlice } from '@reduxjs/toolkit'
import { createSagaAction } from 'saga-toolkit'
const name = 'example'
const initialState = {
result: null,
loading: false,
error: null,
}
export const fetchThings = createSagaAction(`${name}/fetchThings`)
export const doSomeMoreAsyncStuff = createSagaAction(`${name}/doSomeMoreAsyncStuff`)
const slice = createSlice({
name,
initialState,
extraReducers: {
[fetchThings.pending]: () => ({
loading: true,
}),
[fetchThings.fulfilled]: ({ payload }) => ({
result: payload,
loading: false,
}),
[fetchThings.rejected]: ({ error }) => ({
error,
loading: false,
}),
},
})
export default slice.reducer
sagas.js
import { call } from 'redux-saga/effects'
import { takeLatestAsync, takeEveryAsync, putAsync } from 'saga-toolkit'
import API from 'hyper-super-api'
import * as actions from './slice'
function* fetchThings() {
const result = yield call(() => API.get('/things'))
const anotherResult = yield putAsync(actions.doSomeMoreAsyncStuff()) // waits for doSomeMoreAsyncStuff to finish !
return result
}
function* doSomeMoreAsyncStuff() {
...
return 'a value for another result'
}
export default [
takeLatestAsync(actions.fetchThings.pending, fetchThings), // takeLatestAsync: behaves a bit like debounce
takeEveryAsync(actions.doSomeMoreAsyncStuff.pending, doSomeMoreAsyncStuff), // each action will start a new saga thread
]
如果这对任何新来者有帮助,我发现 redux-toolkit 很酷但仍然有点复杂,尤其是当你加入 sagas 时(一开始它本质上是异步魔法和神秘)。我最终做出了类似的东西,我称之为 saga slice。它是 redux 工具包的衍生产品,但从一开始就添加了 sagas。你基本上有你的 reducers,它操纵状态,你的 sagas,它使异步调用和调度其他状态。为了简化您设置的示例,它最终看起来像这样:
import { put } from "redux-saga/effects";
import { createModule } from 'saga-slice';
const environmentSlice = createModule({
name: 'environments',
initialState: [],
reducers: {
getAll: () => {},
getAllSuccess: (state, payload) => {
state.splice(0, state.length);
payload.forEach(data => state.push(data));
},
},
sagas: (A) => ({
*[A.getAll]({ payload }) {
const response = yield call(confApi.admin.getEnvironments());
const { environments } = yield call(getData(response));
yield put(A.getAllSuccess(environments));
}
})
});
这基本上会做您正在尝试做的事情,而没有令人困惑的 watchGetAllEnvironments
和 rootSaga
东西(我觉得这很令人费解)。 Saga 切片可配置为使用 takeEvery
、takeOne
或您想要使用的任何其他效果,而不会太麻烦。它是配置对象,而不是 yield takeEvery()
。我还包含帮助程序,通过包含一个 axios
包装器来简化您正在做的一些事情,该包装器与 saga 的任务取消、流线请求生命周期以及基于标准 REST 原则构建默认 CRUD 模块相关联。查看文档以获得更详尽的解释,并随时打开问题!