安装
安装 Redux Toolkit 和 React Redux
npm install @reduxjs/toolkit react-redux
基本使用
创建Store
在 src/store/index.ts 文件中导入 configureStore 创建一个 Redux Store
// store/index.ts
import { configureStore } from '@reduxjs/toolkit';
export default configureStore({
reducer: {},
});
创建Provider
在src/index.tsx中引入创建的store,在<App> 的外层放置一个 <Provider>,并将 store 作为 prop 传递
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import store from './store';
import { Provider } from 'react-redux';
// 从 React 18 开始
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<App />
</Provider>
);
创建State Slice
import { createSlice } from "@reduxjs/toolkit";
interface CounterState {
value: number;
}
const initialState: CounterState = {
value: 0
};
export const counterSlice = createSlice({
name: "counter",
initialState,
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload;
}
}
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;
将 State Slice Reducer 添加到store
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./reducers/counter";
const store = configureStore({
reducer: {
counter: counterReducer,
},
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export default store;
使用Reducer
引入 useSelector 获取counter reducer中的state,引入 dispatch 触发counter reducer 中事件改变state数据状态。
import { useSelector, useDispatch } from "react-redux";
import { increment, decrement } from "../../store/reducers/counter";
const Home = () => {
const count = useSelector((state: any) => state.counter.value);
const dispatch = useDispatch();
const addNumber1 = () => {
dispatch(increment());
};
const addNumber2 = () => {
dispatch({ type: "counter/increment" });
};
const decrementNumber1 = () => {
dispatch(decrement());
};
const decrementNumber2 = () => {
dispatch({ type: "counter/decrement" });
};
return (
<div>
<h2>Home</h2>
<div>
<div>{count}</div>
<button onClick={addNumber1}>Add Number</button>
<button onClick={addNumber2}>Add Number</button>
<hr />
<button onClick={decrementNumber1}>Decrement Number</button>
<button onClick={decrementNumber2}>Decrement Number</button>
</div>
</div>
)
}
export default Home;
优化调用
上面使用 reducer 的过程无法使用类型约束,如需使用类型约束需要将 RootState 和 AppDispatch 类型导入每个组件过程有些繁琐,可以进行全局封装
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import type { AppDispatch, RootState } from "../store";
// useDispatch
export const useAppDispatch: () => AppDispatch = useDispatch;
// useSelector
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
// store根状态
export type AppRootState = RootState;
优化后的使用方式有类型提示,使用过程更友好
import { increment, decrement } from "../../store/reducers/counter";
import { useAppDispatch, useAppSelector } from '../../hooks'
const Home = () => {
const count = useAppSelector((state) => state.counter.value);
const dispatch = useAppDispatch();
const addNumber1 = () => {
dispatch(increment());
};
const addNumber2 = () => {
dispatch({ type: "counter/increment" });
};
const decrementNumber1 = () => {
dispatch(decrement());
};
const decrementNumber2 = () => {
dispatch({ type: "counter/decrement" });
};
return (
<div>
<h2>Home</h2>
<div>
<div>{count}</div>
<button onClick={addNumber1}>Add Number</button>
<button onClick={addNumber2}>Add Number</button>
<hr />
<button onClick={decrementNumber1}>Decrement Number</button>
<button onClick={decrementNumber2}>Decrement Number</button>
</div>
</div>
)
}
export default Home;
Connect高级组件类型
语法
// redux state
const mapState = (state: RootState) => ({
isOn: state.isOn,
});
// redux actions
const mapDispatch = {
toggleOn: () => ({ type: 'TOGGLE_IS_ON' }),
};
// 自定义类型
interface OwnProps {}
// 组合props类型
type Props = ReturnType<typeof StateProps> & ReturnType<typeof DispatchProps> & OwnProps;
const MyComponent = (props: Props) => {
return <></>
};
export default connect<StateProps, DispatchProps, OwnProps>(
mapState,
mapDispatch
)(MyComponent);
connect 由两个按顺序调用的函数组成:
- 第一个函数接受
mapState和mapDispatch作为参数(可选参数),并返回第二个函数 - 第二个函数接受要被包裹的组件,并返回一个新的包裹组件,该组件将来自
mapState和mapDispatch的 props 向下传递。这两个函数会一起调用,例如connect(mapState, mapDispatch)(MyComponent)
mapStateToProps
mapStateToProps 函数应该返回一个包含组件所需数据的普通对象:
- 对象中的每个字段都将成为实际组件中的 prop
- 字段中的值将用于确定你的组件是否需要重新渲染
import { connect } from "react-redux";
import { AppRootState } from '../../hooks'
const mapStateToProps = (rootState: AppRootState) => {
return {
counterState: rootState.counter,
}
};
// 简写
//const mapStateToProps = (rootState: AppRootState) => ({
// counterState: rootState.counter,
//});
type IProps = ReturnType<typeof mapStateToProps>;
const Home = (props: IProps) => {
console.log("Home", props.counterState);
return (
<div>
<h2>Home</h2>
<div>{props.counterState.value}</div>
</div>
)
}
export default connect(mapStateToProps)(Home);
mapStateToProps 函数的第一个参数是整个 Redux store state,第二个参数 ownProps (可选的)通常用来利用props检索store中的数据。
interface IProps {
id?: number;
}
const mapStateToProps = (rootState: AppRootState, ownProps: IProps) => {
// 组件自定义props
console.log('ownProps',ownProps);
return {
counterState: rootState.counter,
};
};
// 组件使用
<Home id={1} />
mapDispatchToProps
import type { AppRootState, AppDispatchType, } from '../../hooks'
import { connect, ConnectedProps } from "react-redux";
interface IProps {
name?: string
}
const mapDispatchToProps = (dispatch: AppDispatchType) => {
return {
increment: () => dispatch({ type: 'counter/increment' }),
decrement: () => dispatch({ type: 'counter/decrement' })
}
}
type Props = ReturnType<typeof mapStateToProps> & IProps;
const About = (props: Props) => {
return (
<div>
<h2>{props.count}</h2>
<h2 onClick={props.increment}>About</h2>
</div>
)
}
export default connect(null, mapDispatchToProps)(About);
mapDispatchToProps 函数的第一个参数是整个 Redux store actions,第二个参数 ownProps (可选的)通常用来利用props变更时更新store中的数据。
import type { AppRootState, AppDispatchType, } from '../../hooks'
import { connect, ConnectedProps } from "react-redux";
interface IProps {
name?: string
}
const mapDispatchToProps = (dispatch: AppDispatchType, ownProps: IProps) => {
console.log('ownProps',ownProps)
return {
increment: () => dispatch({ type: 'counter/increment' }),
decrement: () => dispatch({ type: 'counter/decrement' })
}
}
type Props = ReturnType<typeof mapStateToProps> & IProps;
const About = (props: Props) => {
return (
<div>
<h2>{props.count}</h2>
<h2 onClick={props.increment}>About</h2>
</div>
)
}
export default connect(null, mapDispatchToProps)(About);
自动类型推断
借助 ConnectedProps _可以实现_自动类型推断,减少类型约束。
import { connect, ConnectedProps } from "react-redux";
import type { AppRootState, AppDispatchType } from '../../hooks'
const mapStateToProps = (rootState: AppRootState) => ({
counterState: rootState.counter,
});
const mapDispatchToProps = (dispatch: AppDispatchType) => ({
addNumber: () => dispatch(increment()),
});
// 传统写法
// type IProps = ReturnType<typeof mapStateToProps> & ReturnType<typeof mapDispatchToProps>;
// 优化后的写法
const connector = connect(mapStateToProps, mapDispatchToProps);
type IProps = ConnectedProps<typeof connector>;
const Home = (props: IProps) => {
return (
<div>
<h2>Home</h2>
</div>
)
}
export default connector(Home);
友情提示
见原文:【React】React Redux)
本文同步自微信公众号 "程序员小溪" ,这里只是同步,想看及时消息请移步我的公众号,不定时更新我的学习经验。