react-redux 可以全局传递 store, 让所有组件都可以直接使用 store 的 state 和 dispatch
官方文档

安装

  1. yarn add react-redux
  2. yarn add -D @types/react-redux

Provider

在入口文件index.tsx中引入Provider和store, 将App组件包裹,则App组件内的所有组件都可以访问store

  1. // ...
  2. import {Provider} from 'react-redux'
  3. import store from './redux/store'
  4. ReactDOM.render(
  5. <Provider store={store}>
  6. <App />
  7. </Provider>,
  8. document.getElementById('root')
  9. )

connect

connect函数可以将 state 和 dispatch 映射到组件的 props 中,组件通过 props 访问 state 和调用 dispatch 方法
connect在类组件中更常用

mapStateToProps

connect接受2个参数,第一个参数是mapStateToProps, 顾名思义,就是把state映射到props上

  1. const mapStateToProps = (state) => {
  2. return {
  3. language: state.language,
  4. languageList: state.languageList
  5. }
  6. }

组件中就可以直接通过props访问到state了

  1. const language = props.language
  2. const languageList = props.languageList

mapDispatchToProps

第二个参数是mapDispatchToProps, 即将 dispatch 映射到props

mapDispatchToProps有2中形式,一种是简单的javascript对象,对象里面是action creator函数,

  1. // import action creator
  2. import {
  3. addLanguageActionCreator,
  4. changeLanguageActionCreator,
  5. } from '../../redux/language/languageActions'
  6. const mapDispatchToProps = {
  7. addLanguageActionCreator,
  8. changeLanguageActionCreator
  9. }

connect将action creator封装成了自带dispatch的形式,通过props调用action creator就触发了dispatch

  1. const menuClickHandler = (e) => {
  2. if (e.key === 'new') {
  3. props.addLanguageActionCreator('新语言', 'new_lang')
  4. } else {
  5. props.changeLanguageActionCreator(e.key)
  6. }
  7. }

mapDispatchToProps的第二种形式是函数,可用于更复杂的情况, 一般对象形式就够用了

  1. const mapDispatchToProps = (dispatch: Dispatch) => {
  2. return {
  3. changeLanguage: (code: "zh" | "en") => {
  4. const action = changeLanguageActionCreator(code);
  5. dispatch(action);
  6. },
  7. addLanguage: (name: string, code: string) => {
  8. const action = addLanguageActionCreator(name, code);
  9. dispatch(action);
  10. },
  11. };
  12. };

Typescript类型

最后,千万别忘了,以上的使用都建立在把组件传递给 connect 的基础上
以下是用TS添加了类型的版本

注意:

  • ReturnType用于获得函数的返回值的类型
  • typescript中 & 运算符得到的是交叉类型,即多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。 ```tsx import { createStore } from ‘redux’ import languageReducer from ‘./language/languageReducer’

const store = createStore(languageReducer)

// 在 store 中导出 state 的类型 RootState export type RootState = ReturnType export default store

  1. ```tsx
  2. import { connect } from 'react-redux'
  3. import { RootState } from '../../redux/store'
  4. import { Dispatch } from "redux";
  5. const mapStateToProps = (state: RootState) => {
  6. return {
  7. language: state.language,
  8. languageList: state.languageList
  9. }
  10. }
  11. const mapDispatchToProps = (dispatch: Dispatch) => {
  12. return {
  13. changeLanguage: (code: "zh" | "en") => {
  14. const action = changeLanguageActionCreator(code);
  15. dispatch(action);
  16. },
  17. addLanguage: (name: string, code: string) => {
  18. const action = addLanguageActionCreator(name, code);
  19. dispatch(action);
  20. },
  21. };
  22. };
  23. type PropsType = ReturnType<typeof mapStateToProps> &
  24. ReturnType<typeof mapDispatchToProps>;
  25. const HeaderComponent:React.FC<PropsType> = (props) => {
  26. const language = props.language
  27. const languageList = props.languageList
  28. const menuClickHandler = (e) => {
  29. if (e.key === 'new') {
  30. props.addLanguage('新语言', 'new_lang')
  31. } else {
  32. props.changeLanguage(e.key)
  33. }
  34. }
  35. return (
  36. // ...
  37. )
  38. }
  39. export const Header = connect(mapStateToProps, mapDispatchToProps)(HeaderComponent)

使用 react-redux 后,组件不需要对 store 进行 subscribe, 当 state 变化时,页面会自动重新渲染

hooks

react-redux 还提供了更加简洁且适合函数式组件使用的hooks来访问 store 的 state 和 dispatch

  • useSelector: 用于在组件内获取 store 的state
  • useDispatch: 用于在组件内获取dispatch方法 ```tsx import { addLanguageActionCreator, changeLanguageActionCreator, } from ‘../../redux/language/languageActions’ import { useSelector, useDispatch } from ‘react-redux’ import { RootState } from ‘../../redux/store’

export const Header:React.FC = () => { const language = useSelector((state:RootState) => state.language) const languageList = useSelector((state:RootState) => state.languageList) const dispatch = useDispatch()

const menuClickHandler = (e) => { if (e.key === ‘new’) { dispatch(addLanguageActionCreator(‘新语言’, ‘new_lang’)) } else { dispatch(changeLanguageActionCreator(e.key)) } }

return ( // … ) }

  1. <a name="PkE33"></a>
  2. ### define typed hooks
  3. [参考文档](https://react-redux.js.org/using-react-redux/usage-with-typescript)<br />因为使用了 typescript, 在组件内使用 useSelector 是需要指定 state 的类型为 RootState<br />为了不用每次使用 useSelector 或 useDispatch 时都要在组件内指定类型,我们可以预先为 useSelector 和useDispatch 指定类型
  4. 先在store.ts中将 state 和 dispatch 的类型导出
  5. ```tsx
  6. import { createStore } from 'redux'
  7. import languageReducer from './language/languageReducer'
  8. const store = createStore(languageReducer)
  9. export default store
  10. export type RootState = ReturnType<typeof store.getState>
  11. export type AppDispatch = typeof store.dispatch

新建redux/hooks.ts文件, 导出新的有类型的 useSelector 和 useDispatch

  1. import { TypedUseSelectorHook,
  2. useDispatch as useReduxDispatch,
  3. useSelector as useReduxSelector} from 'react-redux'
  4. import type { RootState, AppDispatch } from './store'
  5. export const useDispatch = () => useReduxDispatch<AppDispatch>()
  6. export const useSelector: TypedUseSelectorHook<RootState> = useReduxSelector

在组件中都改用 hooks.ts 中的 useSelector 和 useDispatch 就不用再指定类型了

  1. import { useSelector, useDispatch } from '../../redux/hooks'
  2. export const Header:React.FC = () => {
  3. const language = useSelector((state) => state.language)
  4. const languageList = useSelector((state) => state.languageList)
  5. const dispatch = useDispatch()
  6. // ...