记录一下React+TypeScript如何使用redux
一、截至本篇文章发布依赖均为最新版本,版本号如下
"@types/react": "^17.0.37",
"@types/react-dom": "^17.0.11",
"@types/react-redux": "^7.1.20",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-redux": "^7.2.6",
"react-router-dom": "^6.0.2",
"react-scripts": "4.0.3",
"redux": "^4.1.2",
"redux-devtools-extension": "^2.13.9",
"typescript": "^4.1.2",
二、初始化redux
本文主要记录redux使用react+ts环境搭建可参照其他文章
1、安装依赖
yarn add redux react-redux @types/react-redux redux-devtools-extension
2、新建目录结构如下
3、store代码如下
- store入口文件 /store/index.ts
import { applyMiddleware, createStore } from "redux";
import { composeWithDevTools } from "redux-devtools-extension"; /* 使用redux调试工具 */
import reducers from "./reducers";
export type rootState = ReturnType<typeof reducers>;
const store = createStore(reducers, composeWithDevTools(applyMiddleware()));
export default store;
- reducer入口文件 /store/reducers/index.ts
import { combineReducers } from "redux";
import user from "./user";
export default combineReducers({ user });
- user模块reducer /store/reducers/user.ts
export interface IUser {
id: string;
name: string;
role: string;
}
export interface IUserState {
user: IUser;
}
const initUserState: IUserState = {
/* state默认值 */
user: {
id: "default",
name: "普通用户",
role: "user",
},
};
export enum IUserActionType {
/* Actions */
INIT,
CHANGE,
}
const user = (
state: IUserState = initUserState,
action: { type: IUserActionType; payload: any }
) => {
const { payload } = action;
switch (action.type) {
case IUserActionType.INIT:
return state;
case IUserActionType.CHANGE:
return { ...state, user: { ...state.user, ...payload } };
default:
return state;
}
};
export default user;
4、在项目入口文件index.tsx内注入store
import React from 'react';
import ReactDOM from 'react-dom';
import '@/assets/styles/default.less';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-redux'; /* redux使用 */
import store from './store'; /* redux使用 */
ReactDOM.render(
<Provider store={store}> /* redux使用 */
<React.StrictMode>
<App />
</React.StrictMode>
</Provider>,
document.getElementById('root')
);
reportWebVitals();
三、在组件中使用
- action最好提取到统一文件内方便复用,本文为方便演示未提取
1、在类组件中使用
/* 类组件使用redux */
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { Dispatch } from 'redux'
import { rootState } from '@/store'
import { IUser, IUserActionType } from '@/store/reducers/user'
interface IProps {
user: IUser
changeName?: (name: string) => void
}
class ClassComp extends Component<IProps> {
protected handleChangeName(name: string) {
this.props.changeName && this.props.changeName(name)
}
render() {
const { name } = this.props.user
return (
<div>
{name}
<br />
<button onClick={() => { this.handleChangeName("张三") }}>ClassComp改变名字</button>
</div>
)
}
}
const mapStateToProps = (state: rootState) => {
return { ...state.user }
}
const mapDispatchToProps = (dispatch: Dispatch) => ({
changeName: (name: string) => {
dispatch({
type: IUserActionType.CHANGE,
payload: { name }
})
}
})
export default connect(mapStateToProps, mapDispatchToProps)(ClassComp)
2、在函数式组件中使用
/* 函数式组件使用redux */
import React from 'react'
import { connect } from 'react-redux'
import { Dispatch } from 'redux'
import { rootState } from '@/store'
import { IUser, IUserActionType } from '@/store/reducers/user'
interface IProps {
user: IUser
changeName?: (name: string) => void
}
function FnComp(props: IProps) {
const { user: { name }, changeName } = props
const handleChangeName = (name: string) => {
changeName && changeName(name)
}
return (
<div>
{name}
<br />
<button onClick={() => { handleChangeName("李四") }}>FnComp改变名字</button>
</div>
)
}
const mapStateToProps = (state: rootState) => {
return { ...state.user }
}
const mapDispatchToProps = (dispatch: Dispatch) => ({
changeName: (name: string) => {
dispatch({
type: IUserActionType.CHANGE,
payload: { name }
})
}
})
export default connect(mapStateToProps, mapDispatchToProps)(FnComp)
3、配合Hook使用
/* Hook使用redux */
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { rootState } from '@/store/index'
import { IUserState, IUserActionType } from '@/store/reducers/user'
export default function HookComp() {
const { user } = useSelector<rootState, IUserState>((state) => state.user)
const dispatch = useDispatch()
const handleChangeName = (name: string) => {
dispatch({
type: IUserActionType.CHANGE,
payload: { name }
})
}
return (
<div>
{user.name}
<br />
<button onClick={() => { handleChangeName("王五") }}>HookComp改变名字</button>
</div>
)
}