如何使用 Redux Toolkit 设置 Redux
开发现代 Web 应用程序不仅涉及 UI 构建,还涉及状态管理。Redux 是最广泛使用的 Redux 库之一。在本教程中,您将学习如何使用 2020 年最新的库和技术以及 Redux Toolkit 来设置 Redux,这将简化您的逻辑并确保您的设置具有良好的默认配置。
为什么要选择 Redux Toolkit
Redux 是入门的良好基础,但为了简化操作,建议使用 Redux Toolkit。它的创建旨在帮助解决关于 Redux 的三个常见问题:
- 配置 Redux store 太复杂了
- “我必须添加很多包才能让 Redux 发挥任何有用的作用。”
- “Redux 需要编写太多样板代码”
它具备符合Redux 最佳实践的功能。它包含多个实用函数,可简化最常见的 Redux 用例,包括 store 设置、定义 reducer、使用 Immer 实现不可变更新逻辑,甚至允许一次性创建整个状态“切片”,而无需编写 action creator。
它预配置了最常用的 Redux 插件,例如用于异步逻辑的 Redux Thunk 和用于编写选择器函数的 Reselect,方便您立即使用。它还允许您覆盖所有设置,例如,您可以轻松地将其与 redux-saga 或任何其他中间件一起使用。
如何配置带有 Redux 的 Create React App
本 Redux 教程首先让我们使用 CRA 设置一个新的 React 应用程序:
npm install -g create-react-app
create-react-app redux-tutorial
cd redux-tutorial
接下来我们将添加 Redux:
npm install --save react-redux @reduxjs/toolkit
首先配置 store。创建文件 src/store/index.js,内容如下:
import { configureStore } from '@reduxjs/toolkit'
import { combineReducers } from 'redux'
const reducer = combineReducers({
// here we will be adding reducers
})
const store = configureStore({
reducer,
})
export default store;
`configureStore` 接受单个对象而不是多个函数参数。这是因为底层 store 已配置为允许使用 Redux DevTools 扩展,并且默认包含了一些 Redux 中间件。
接下来,我们需要将我们的 store 连接到 React 应用。像这样将其导入到 index.js 中:
...
import { Provider } from 'react-redux'
import store from './store'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
Provider 封装了 App,整个应用程序都可以访问 Redux。如果您使用 `npm start` 启动应用程序并打开 Redux 开发工具,应该会看到 `@@INIT`。恭喜,您已成功配置 Redux!
如何构建你的 Redux
现在我们来设置 Redux 身份验证,并实现一个简单的登录表单和登录后显示的注销按钮。Redux 本身并不关心应用程序的文件夹和文件结构。但是,将特定功能的逻辑放在一起通常更容易维护代码。Redux.org 建议大多数应用程序应该使用“功能文件夹”模式(将某个功能的所有文件放在同一个文件夹中)或“鸭子模式”(将某个功能的所有 Redux 逻辑放在单个文件中)来组织文件,而不是按代码“类型”(reducer、action 等)将逻辑分散到不同的文件夹中。
让我们添加 src/store/user.js 存储切片:
import { createSlice } from '@reduxjs/toolkit'
// Slice
const slice = createSlice({
name: 'user',
initialState: {
user: null,
},
reducers: {
loginSuccess: (state, action) => {
state.user = action.payload;
},
logoutSuccess: (state, action) => {
state.user = null;
},
},
});
export default slice.reducer
// Actions
const { loginSuccess, logoutSuccess } = slice.actions
export const login = ({ username, password }) => async dispatch => {
try {
// const res = await api.post('/api/auth/login/', { username, password })
dispatch(loginSuccess({username}));
} catch (e) {
return console.error(e.message);
}
}
export const logout = () => async dispatch => {
try {
// const res = await api.post('/api/auth/logout/')
return dispatch(logoutSuccess())
} catch (e) {
return console.error(e.message);
}
}
商店功能文件包含 createSlice,它返回一个“slice”对象,该对象包含一个名为 reducer 的字段,其中包含生成的 reducer 函数,以及一个名为 actions 的对象,其中包含生成的 action creators。
在底部,我们可以导入操作创建器并直接导出它们,或者在异步操作(如登录和注销)中使用它们。
要将 reducer 连接到 Redux,我们需要将其添加到 store/index.js 中的主 reducer 中:
...
import user from './user'
const reducer = combineReducers({
user,
})
使用 useDispatch 和 useSelector 将 Redux 连接到组件
我们的 Redux 配置已经完成。现在让我们配置身份验证表单。为此,我们将使用 Formik。请在终端中输入以下命令:
npm install --save formik
现在我们可以创建以下 src/App.js 组件:
...
import {useDispatch, useSelector} from 'react-redux'
import {Field, Form, Formik} from 'formik'
import {login, logout} from './store/user'
function App() {
const dispatch = useDispatch()
const { user } = useSelector(state => state.user)
if (user) {
return (
<div>
Hi, {user.username}!
<button onClick={() => dispatch(logout())}>Logout</button>
</div>
)
}
return (
<div>
<Formik
initialValues={{ username: '', password: '' }}
onSubmit={(values) => { dispatch(login(values)) }}
>
{({ isSubmitting }) => (
<Form>
<Field type="text" name="username" />
<Field type="password" name="password" />
<button type="submit" disabled={isSubmitting}>Login</button>
</Form>
)}
</Formik>
</div>
);
}
请注意,这里不再需要 connect!有了 useDispatch 和 useSelector,我们现在可以使用 hooks 将 Redux 集成到纯组件中!我们只需要用 Provider 包裹 App,而且相比 connect,样板代码要少得多。
如何在页面重新加载时保持用户身份验证
您可能已经注意到,每次页面重新加载时,身份验证都会被重置。
使用 localStorage 可以轻松解决这个问题,只需在 src/store/user.js 文件中添加几行代码即可。
+const initialUser = localStorage.getItem('user')
+ ? JSON.parse(localStorage.getItem('user'))
+ : null
+
const slice = createSlice({
name: 'user',
initialState: {
- user: null,
+ user: initialUser,
},
reducers: {
loginSuccess: (state, action) => {
state.user = action.payload;
+ localStorage.setItem('user', JSON.stringify(action.payload))
},
logoutSuccess: (state, action) => {
state.user = null;
+ localStorage.removeItem('user')
},
},
});
如何存储令牌
我最喜欢的 API 客户端库是 Axios。我更喜欢 Axios 而不是内置 API,因为它易于使用,而且还有一些额外的功能,例如 XSRF 令牌支持和拦截器。
以下是我常用的一个配置示例:
const api = axios.create({
baseURL: '/',
headers: {
'Content-Type': 'application/json'
},
})
api.interceptors.request.use(
config => {
const token = localStorage.getItem('token')
if (token) {
config.headers['Authorization'] = `Token ${token}`
}
return config
},
error => Promise.reject(error)
)
export default api
登录后如何重定向
在 Redux 操作后重定向用户的最简单方法是使用 React 提供的 Redirect 组件。
这可以是登录表单组件中的一个示例,例如使用如下代码:
if (user) {
return (
<Redirect to={'/home'} />
)
}