中间件Redux Saga的用途是什么
什么是Redux Saga
Redux Saga是一个用于Redux的中间件库,提供了一种优雅的方式来处理React应用中的副作用。副作用可以包括异步操作,如进行API调用、管理WebSocket连接等。
在我们深入实现细节之前,让我们对Redux Saga有一个基本的了解。
Redux Saga是基于ES6引入的生成器的概念构建的。生成器是特殊的函数,可以暂停和恢复,使其非常适合以非阻塞方式处理异步操作。Redux Saga使用这些生成器以声明式和可测试的方式管理副作用。
带有中间件的Redux工作流程:
Redux-Saga是一个旨在使应用程序副作用(例如,异步的数据获取和不纯的访问浏览器缓存)更容易管理、更高效执行、易于测试和更好处理故障的库。
设置Redux Saga
步骤1: 创建一个React应用程序
npx create-react-app saga-app
步骤2:安装所需软件包
cd saga-app
npm install @reduxjs/toolkit react-redux redux-saga
项目结构
现在在src目录下的一个actions目录中创建一个index.js文件。
步骤3:实现Redux Saga
saga-app是一个基于React的Web应用程序,展示了Redux Saga,这是一个用于处理异步任务的中间件。它从JSONPlaceholderAPI获取数据,JSONPlaceholderAPI是一个用于测试的模拟RESTful API,并将数据显示在Web页面上。用户可以删除显示的数据。JSONPlaceholderAPI提供模拟数据,非常适合测试和开发。该应用程序演示了如何在React应用程序中使用Redux Saga有效地管理异步操作。
动作
在actions/index.js文件中创建动作创建器。这些动作将用于触发saga。
// actions/index.js
export const fetchDataRequest = () => ({
type: "FETCH_DATA_REQUEST",
});
export const fetchDataSuccess = (data) => ({
type: "FETCH_DATA_SUCCESS",
payload: data,
});
export const fetchDataFailure = (error) => ({
type: "FETCH_DATA_FAILURE",
payload: error,
});
export const deleteDataRequest = () => ({
type: "DELETE_DATA_REQUEST",
});
Reducers
在reducers/index.js文件中创建reducers来根据actions管理状态。
// reducers/index.js
const initialState = {
data: null,
error: null,
};
const dataReducer = (state = initialState, action) => {
switch (action.type) {
case "FETCH_DATA_SUCCESS":
return {
...state,
data: action.payload,
error: null,
};
case "FETCH_DATA_FAILURE":
return {
...state,
data: null,
error: action.payload,
};
case "DELETE_DATA_REQUEST":
return {
...state,
data: null,
error: null,
};
default:
return state;
}
};
export default dataReducer;
Sagas
在sagas/index.js文件中,你可以定义你的sagas。Sagas是监视特定动作并执行异步操作的生成器函数。
// sagas/index.js
import { takeEvery, put, call } from "redux-saga/effects";
import * as actions from "../actions";
// Simulate an API call
const fetchDataFromAPI = async () => {
try {
const response = await fetch(
"https://jsonplaceholder.typicode.com/todos/1"
);
const data = await response.json();
return data;
} catch (error) {
throw error;
}
};
function* fetchData() {
try {
const data = yield call(fetchDataFromAPI);
yield put(actions.fetchDataSuccess(data));
} catch (error) {
yield put(actions.fetchDataFailure(error.message));
}
}
export function* watchFetchData() {
yield takeEvery("FETCH_DATA_REQUEST", fetchData);
}
将Redux Saga连接到您的应用程序
在您的index.js文件中,使用中间件设置Redux存储以包含Redux Saga。
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { configureStore } from "@reduxjs/toolkit";
import { Provider } from "react-redux";
import createSagaMiddleware from "redux-saga";
import rootReducer from "./reducers";
import { watchFetchData } from "./sagas";
const sagaMiddleware = createSagaMiddleware();
const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(sagaMiddleware),
});
sagaMiddleware.run(watchFetchData);
const root = ReactDOM.createRoot(
document.getElementById("root")
);
root.render(
<Provider store={store}>
<React.StrictMode>
<App />
</React.StrictMode>
</Provider>
);
// If you want to start measuring performance in
// your app, pass a function to log results
// (for example: reportWebVitals(console.log))
// or send to an analytics endpoint.
使用Redux Saga在组件中
现在,您可以在组件中使用Redux Saga来触发异步操作。在您的组件/MyComponent.js文件中:
//components/myComponents.js
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import {
fetchDataRequest,
deleteDataRequest,
} from "../actions";
const MyComponent = () => {
const dispatch = useDispatch();
const data = useSelector((state) => state.data);
const error = useSelector((state) => state.error);
useEffect(() => {
dispatch(fetchDataRequest());
}, [dispatch]);
const handleDeleteData = () => {
dispatch(deleteDataRequest());
};
return (
<div className="app-container">
<h1>Redux Saga App</h1>
<div className="data-container">
{data ? (
<div className="data">
{JSON.stringify(data)}
</div>
) : (
<div className="loading">
{error
? `Error: ${error}`
: "Loading data..."}
</div>
)}
</div>
<button
className="fetch-button"
onClick={() => dispatch(fetchDataRequest())}
>
Fetch Data
</button>
<button
className="delete-button"
onClick={handleDeleteData}
>
Delete Data
</button>
</div>
);
};
export default MyComponent;
App.js
import "./App.css";
import MyComponent from "./components/myComponent";
function App() {
return <MyComponent />;
}
export default App;
App.css
/* App.css */
.app-container {
text-align: center;
margin: 20px;
padding: 20px;
border: 1px solid #ccc;
border-radius: 5px;
background-color: #f8f8f8;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
h1 {
font-size: 24px;
margin-bottom: 20px;
}
.data-container {
margin: 20px 0;
}
.data {
font-size: 16px;
background-color: #e0e0e0;
padding: 10px;
border-radius: 5px;
}
.loading {
font-size: 16px;
color: #777;
padding: 10px;
}
.fetch-button {
background-color: #007bff;
color: #fff;
padding: 10px 20px;
font-size: 16px;
border: none;
border-radius: 5px;
cursor: pointer;
}
.fetch-button:hover {
background-color: #0056b3;
}
.delete-button {
background-color: #dc3545;
color: #fff;
padding: 10px 20px;
font-size: 16px;
border: none;
border-radius: 5px;
cursor: pointer;
margin-top: 10px;
}
.delete-button:hover {
background-color: #c82333;
}
第4步:使用npm命令运行应用程序,输出结果将会在 http://localhost:3000/
npm start
输出: