React redux saga 基本使用流程
redux-saga 是一个用于管理redux应用异步操作代替 redux-thunk 的中间件
集中处理 redux 副作用问题。reducer负责处理action的更新,saga负责协调那些复杂或者异步的操作
本文主要描述联合使用redux 和saga模式的方法,以及单独使用react-redux 和redux-saga的方法
用自己的话捋一遍二者的流程和融合使用方式
1.首先我们先捋一遍
react-redux
的执行流程在需要使用redux的组件中,我们需要使用connect对组件进行连接,那么具体连接什么呢?
这里需要提到两个重要的对象:
mapStateToProps
和mapDispatchToProps
,见名知意,将state和action转为props传入被包裹的组件,这样就可以从props接收到了,
注意,这里的state是redux管理的,通常会由各自的reducer管理,也就是state.counterReducer.count
这样子,同时这个state的初始状态也是在reducer中定义的。2.那么,当点击事件触发,就会执行increment和decrement,它们是两个
action
这个reducer会被发到reducer中进行挨个匹配,要注意重名的情况哟,将初始state作为参数传入reducer函数,action就是上面发来的,这里使用
action.type
判断是哪个action,使用payload
获取传来的数据,注意在reducer处理action后,我们需要返回的是经过操作更新后的新状态即可
3.然后我们来看一下
redux-saga
又怎么用呢,当然首先要记得在store中添加saga中间件,我们还是从一个请求例子开始这里发起了一个action,那么谁会来处理呢?
4.
Redux-Saga Workers配合根 Saga
会进行处理,在worker中监听action,并指定对应的处理函数,使用yield和put,take,call等方法实现工作器函数的逻辑,那么worker监听action由rootSaga来进行管理,使用all方面添加需要监听的saga,这个rootSage需要在store配置中间件时_// 运行根 Saga_ sagaMiddleware.run(rootSaga);5.那么工作器函数的逻辑也只是put了一个action而已,目的只是为了组合逻辑,最后还是要reducer来处理的
jsx
import { connect } from 'react-redux';
import { increment, decrement } from './actions';
const Counter = (props) => {
{ count, increment, decrement } = props
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
};
const mapStateToProps = (state) => ({
count: state.counterReducer.count,
});
const mapDispatchToProps = {
increment,
decrement,
};
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
jsx
// actions.js
export const increment = () => ({
type: 'INCREMENT',
});
export const decrement = () => ({
type: 'DECREMENT',
});
export const change = (item) => {
return {
type:"change-city",
payload:item
}
}
jsx
// reducers.js
const initialState = {
count: 0,
};
const counterReducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT':
return {
...state,
count: state.count + 1,
};
case 'DECREMENT':
return {
...state,
count: state.count - 1,
};
default:
return state;
}
};
export default counterReducer;
jsx
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
const DataComponent = () => {
const dispatch = useDispatch();
const data = useSelector(state => state.data.data);
const isLoading = useSelector(state => state.data.isLoading);
useEffect(() => {
// 在组件渲染时触发异步请求
dispatch({ type: 'FETCH_DATA' });
}, [dispatch]);
// 渲染数据
return (
<div>内容</div>
);
};
export default DataComponent;
jsx
// src/sagas/data.js worker
import { call, put, takeEvery } from 'redux-saga/effects';
import api from '../api';
// 定义异步请求函数
const fetchDataFromApi = async () => {
// 这里可以使用真实的异步请求
const response = await api.getData();
return response;
};
// 定义工作器函数,处理异步请求流程
function* fetchDataSaga() {
try {
yield put({ type: 'FETCH_DATA_REQUEST' });
const data = yield call(fetchDataFromApi);
yield put({ type: 'FETCH_DATA_SUCCESS', payload: data });
} catch (error) {
yield put({ type: 'FETCH_DATA_FAILURE', payload: error.message });
}
}
// 监听触发异步请求的动作,并调用相应的工作器函数
export function* watchFetchData() {
yield takeEvery('FETCH_DATA', fetchDataSaga);
}
jsx
// src/sagas/index.js rootSaga
import { all } from 'redux-saga/effects';
import { watchFetchData } from './saga/data';
// 在这里引入其他需要监听的 Saga 文件
// 根 Saga,将多个 Saga 组合在一起
export default function* rootSaga() {
yield all([watchFetchData()]);
// 在这里添加其他需要监听的 Saga
}
jsx
// src/reducers/data.js
// 定义初始状态
const initialState = {
isLoading: false,
data: null,
error: null,
};
// 定义对应的 Reducer
export const reducer = (state = initialState, action) => {
switch (action.type) {
case 'FETCH_DATA_REQUEST':
return {
...state,
isLoading: true,
error: null,
};
case 'FETCH_DATA_SUCCESS':
return {
...state,
isLoading: false,
data: action.payload,
};
case 'FETCH_DATA_FAILURE':
return {
...state,
isLoading: false,
error: action.payload,
};
default:
return state;
}
};
Redux-Saga 全流程示例
Redux-Saga 是一个用于管理应用程序副作用(例如异步请求和数据获取)的库。下面是一个简单的 Redux-Saga 全流程示例:
- 安装 Redux 和 Redux-Saga:
jsx
npm install redux redux-saga
- 创建 Redux Store,包括创建 Redux-Saga 中间件:
jsx
import { createStore, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga';
import rootReducer from './reducers';
import rootSaga from './sagas';
// 创建 Saga 中间件
const sagaMiddleware = createSagaMiddleware();
// 创建 Redux Store,并应用 Saga 中间件
const store = createStore(
rootReducer,
applyMiddleware(sagaMiddleware)
);
// 运行根 Saga
sagaMiddleware.run(rootSaga);
export default store;
- 创建 Redux Reducers:
jsx
// src/reducers/index.js
import { combineReducers } from 'redux';
import { reducer as dataReducer } from './data';
const rootReducer = combineReducers({
// 将各个子 Reducer 组合在一起
data: dataReducer,
});
export default rootReducer;
- 创建 Redux-Saga Schemas:
jsx
// src/reducers/data.js
// 定义初始状态
const initialState = {
isLoading: false,
data: null,
error: null,
};
// 定义对应的 Reducer
export const reducer = (state = initialState, action) => {
switch (action.type) {
case 'FETCH_DATA_REQUEST':
return {
...state,
isLoading: true,
error: null,
};
case 'FETCH_DATA_SUCCESS':
return {
...state,
isLoading: false,
data: action.payload,
};
case 'FETCH_DATA_FAILURE':
return {
...state,
isLoading: false,
error: action.payload,
};
default:
return state;
}
};
- 创建 Redux-Saga Workers:
jsx
// src/sagas/data.js
import { call, put, takeEvery } from 'redux-saga/effects';
import api from '../api';
// 定义异步请求函数
const fetchDataFromApi = async () => {
// 这里可以使用真实的异步请求
const response = await api.getData();
return response;
};
// 定义工作器函数,处理异步请求流程
function* fetchDataSaga() {
try {
yield put({ type: 'FETCH_DATA_REQUEST' });
const data = yield call(fetchDataFromApi);
yield put({ type: 'FETCH_DATA_SUCCESS', payload: data });
} catch (error) {
yield put({ type: 'FETCH_DATA_FAILURE', payload: error.message });
}
}
// 监听触发异步请求的动作,并调用相应的工作器函数
export function* watchFetchData() {
yield takeEvery('FETCH_DATA', fetchDataSaga);
}
- 创建根 Saga:
jsx
// src/sagas/index.js
import { all } from 'redux-saga/effects';
import { watchFetchData } from './data';
// 在这里引入其他需要监听的 Saga 文件
// 根 Saga,将多个 Saga 组合在一起
export default function* rootSaga() {
yield all([watchFetchData()]);
// 在这里添加其他需要监听的 Saga
}
- 使用 Redux-Saga 发起异步请求:
jsx
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
const DataComponent = () => {
const dispatch = useDispatch();
const data = useSelector(state => state.data.data);
const isLoading = useSelector(state => state.data.isLoading);
useEffect(() => {
// 在组件渲染时触发异步请求
dispatch({ type: 'FETCH_DATA' });
}, [dispatch]);
// 渲染数据
return (
<div>
{isLoading ? (
<p>Loading...</p>
) : data ? (
<p>{data}</p>
) : (
<p>No data available</p>
)}
</div>
);
};
export default DataComponent;
这是一个基本的 Redux-Saga 全流程示例,包括创建 Redux Store、定义 Reducers、编写 Saga 和工作器函数,以及在组件中使用 Redux-Saga 发起异步请求。
下面是 Redux-Saga 的工作流程:
- 创建 Saga 中间件:在创建 Redux Store 之前,先创建 Redux-Saga 中间件。使用
createSagaMiddleware
函数来创建中间件实例。- 创建根 Saga:编写根 Saga 函数,用于管理各个子 Saga。根 Saga 是一个 Generator 函数,通过使用
yield all([...])
将多个子 Saga 组合在一起。- 编写 Worker 函数:在单独的 Saga 文件中,编写用于处理具体异步任务的 Worker 函数。Worker 函数是一个 Generator 函数,并且会被根 Saga 监听特定的 action。
- 监听并触发 Action:在根 Saga 中使用
takeEvery
或takeLatest
等 effect 函数来监听指定的 action,一旦该 action 被触发,对应的 Worker 函数将被执行。- 处理异步逻辑:在 Worker 函数中,使用 Redux-Saga 提供的各种 effect 函数来处理异步逻辑。例如,使用
call
来调用异步函数,使用put
来派发新的 action。- 更新 Redux Store:在 Worker 函数中,使用
put
effect 来派发新的 action,通知 Redux Store 更新状态。这些新的 action 可以被其他 Reducer 处理,从而改变应用程序的状态。- 监听并取消异步任务:Redux-Saga 还提供了
take
和cancel
等 effect 函数,用于监听特定的 action 并取消正在进行的异步任务。
通过以上流程,Redux-Saga 能够管理应用程序中的异步逻辑,并与 Redux Store 进行交互,以保持状态更新和一致性。
React-Redux 全流程示例
下面是一个简单的 React-Redux 全流程示例,包括创建 Redux Store、定义 Actions、编写 Reducers、使用 Connect 连接组件以及在组件中触发 Actions 和访问状态:
- 创建 Redux Store:
jsx
import { createStore } from 'redux';
import rootReducer from './reducers';
const store = createStore(rootReducer);
- 定义 Actions:
jsx
// actions.js
export const increment = () => ({
type: 'INCREMENT',
});
export const decrement = () => ({
type: 'DECREMENT',
});
- 编写 Reducers:
jsx
// reducers.js
const initialState = {
count: 0,
};
const counterReducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT':
return {
...state,
count: state.count + 1,
};
case 'DECREMENT':
return {
...state,
count: state.count - 1,
};
default:
return state;
}
};
export default counterReducer;
- 使用 Connect 连接组件:
jsx
import { connect } from 'react-redux';
import { increment, decrement } from './actions';
const Counter = ({ count, increment, decrement }) => {
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
};
const mapStateToProps = (state) => ({
count: state.count,
});
const mapDispatchToProps = {
increment,
decrement,
};
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
- 在组件中触发 Actions 和访问状态:
jsx
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store';
import Counter from './Counter';
ReactDOM.render(
<Provider store={store}>
<Counter />
</Provider>,
document.getElementById('root')
);
在上面的例子中,创建了一个 Redux Store,并定义了两个简单的 Actions(increment 和 decrement)。然后编写了 Reducer 来处理这些 Actions 并更新应用程序的状态。 接下来使用 Connect 函数连接 Counter 组件与 Redux Store,并通过 mapStateToProps 将需要的状态映射到组件的 props 中,通过 mapDispatchToProps 将 Actions 映射到组件的 props 中。最后,在根组件中使用 Provider 包裹 Counter 组件,并传入 Redux Store。
这样,在 Counter 组件中就可以通过 props 访问 count 状态,并通过调用 increment 和 decrement 方法来触发对应的 Actions,从而实现状态的更新和响应。
- 创建 Redux Store:首先,需要创建一个 Redux Store 来存储应用程序的状态。通过
createStore
函数可以创建一个全局唯一的状态树,并将 Reducers 和中间件传递给 createStore。- 定义 Actions:Actions 是描述状态变化的纯 JavaScript 对象。每个 Action 都必须包含一个
type
字段来表示该动作的类型,并且可以包含其他任意字段用于传递数据。可以通过编写 Action Creator 函数来创建 Action,这些函数返回一个 Action 对象。- 编写 Reducers:Reducers 是纯函数,用于根据 Action 的类型更新状态。Reducer 接收当前的状态和要处理的 Action,并根据 Action 类型进行相应的状态更新操作,然后返回一个新的状态对象。注意,Reducer 必须是纯函数,即给定相同的输入,始终产生相同的输出,不会对原有的状态进行修改。
- 使用 Connect 连接组件:为了在 React 组件中访问 Redux Store 中的状态以及触发 Actions,需要使用 Redux 的
connect
函数将组件连接到 Redux Store。connect
函数接收两个参数:mapStateToProps
和mapDispatchToProps
。mapStateToProps
函数将 Redux Store 中的状态映射到组件的 props,而mapDispatchToProps
函数将 Action Creators 映射到组件的 props。- 在组件中触发 Actions 和访问状态:通过连接到 Redux Store 的组件可以访问映射后的状态和 Actions。组件可以使用
props
中的状态来展示数据,并通过调用props
中的 Action Creators 来触发对应的动作,从而更新 Redux Store 中的状态。- Redux Store 的更新和通知:当组件触发了一个 Action 后,Redux Store 会根据 Reducers 的逻辑进行状态更新。Reducers 计算出新的状态并返回给 Redux Store。Redux Store 将新的状态更新到自己身上,并通知已经注册的监听器(例如 React 组件),以便它们可以获取最新的状态并重新渲染。