Taro 使用 Redux

2021-09-30 17:53 更新

在 Taro 中可以自由地使用 React 生態(tài)中非常流行的數(shù)據(jù)流管理工具 Redux 來(lái)解決復(fù)雜項(xiàng)目的數(shù)據(jù)管理問(wèn)題。

首先請(qǐng)安裝 redux 、 @tarojs/redux@tarojs/redux-h5,以及一些需要用到的 redux 中間件

  1. $ yarn add redux @tarojs/redux @tarojs/redux-h5 redux-thunk redux-logger
  2. # 或者使用 npm
  3. $ npm install --save redux @tarojs/redux @tarojs/redux-h5 redux-thunk redux-logger

隨后可以在項(xiàng)目 src 目錄下新增一個(gè) store 目錄,在目錄下增加 index.js 文件用來(lái)配置 store,按自己喜好設(shè)置 redux 的中間件,例如下面例子中使用 redux-thunkredux-logger 這兩個(gè)中間件

  1. // src/store/index.js
  2. import { createStore, applyMiddleware } from 'redux'
  3. import thunkMiddleware from 'redux-thunk'
  4. import { createLogger } from 'redux-logger'
  5. import rootReducer from '../reducers'
  6. const middlewares = [
  7. thunkMiddleware,
  8. createLogger()
  9. ]
  10. export default function configStore () {
  11. const store = createStore(rootReducer, applyMiddleware(...middlewares))
  12. return store
  13. }

接下來(lái)在項(xiàng)目入口文件 app.js 中使用 @tarojs/redux 中提供的 Provider 組件將前面寫(xiě)好的 store 接入應(yīng)用中

  1. // src/app.js
  2. import Taro, { Component } from '@tarojs/taro'
  3. import { Provider } from '@tarojs/redux'
  4. import configStore from './store'
  5. import Index from './pages/index'
  6. import './app.scss'
  7. const store = configStore()
  8. class App extends Component {
  9. render() {
  10. return (
  11. <Provider store={store}>
  12. {this.props.children}
  13. </Provider>
  14. )
  15. }
  16. }
  17. Taro.render(<App />, document.getElementById('app'))

然后就可以開(kāi)始使用了。如 redux 推薦的那樣,可以增加

  • constants 目錄,用來(lái)放置所有的 action type 常量
  • actions 目錄,用來(lái)放置所有的 actions
  • reducers 目錄,用來(lái)放置所有的 reducers

例如我們要開(kāi)發(fā)一個(gè)簡(jiǎn)單的加、減計(jì)數(shù)器功能

新增 action type

  1. // src/constants/counter.js
  2. export const ADD = 'ADD'
  3. export const MINUS = 'MINUS'

新增 reducer 處理

  1. // src/reducers/counter.js
  2. import { ADD, MINUS } from '../constants/counter'
  3. const INITIAL_STATE = {
  4. num: 0
  5. }
  6. export default function counter (state = INITIAL_STATE, action) {
  7. switch (action.type) {
  8. case ADD:
  9. return {
  10. ...state,
  11. num: state.num + 1
  12. }
  13. case MINUS:
  14. return {
  15. ...state,
  16. num: state.num - 1
  17. }
  18. default:
  19. return state
  20. }
  21. }
  1. // src/reducers/index.js
  2. import { combineReducers } from 'redux'
  3. import counter from './counter'

  4. export default combineReducers({
  5. counter
  6. })

新增 action 處理

  1. // src/actions/counter.js
  2. import {
  3. ADD,
  4. MINUS
  5. } from '../constants/counter'
  6. export const add = () => {
  7. return {
  8. type: ADD
  9. }
  10. }
  11. export const minus = () => {
  12. return {
  13. type: MINUS
  14. }
  15. }
  16. // 異步的 action
  17. export function asyncAdd () {
  18. return dispatch => {
  19. setTimeout(() => {
  20. dispatch(add())
  21. }, 2000)
  22. }
  23. }

最后,我們可以在頁(yè)面(或者組件)中進(jìn)行使用,我們將通過(guò) tarojs/redux 提供的 connect 方法將 redux 與我們的頁(yè)面進(jìn)行連接

  1. // src/pages/index/index.js
  2. import Taro, { Component } from '@tarojs/taro'
  3. import { View, Text } from '@tarojs/components'
  4. import { connect } from '@tarojs/redux'
  5. import './index.scss'
  6. import { add, minus, asyncAdd } from '../../actions/counter'
  7. @connect(({ counter }) => ({
  8. counter
  9. }), (dispatch) => ({
  10. add () {
  11. dispatch(add())
  12. },
  13. dec () {
  14. dispatch(minus())
  15. },
  16. asyncAdd () {
  17. dispatch(asyncAdd())
  18. }
  19. }))
  20. class Index extends Component {
  21. config = {
  22. navigationBarTitleText: '首頁(yè)'
  23. }
  24. render () {
  25. return (
  26. <View className='todo'>
  27. <Button className='add_btn' onClick={this.props.add}>+</Button>
  28. <Button className='dec_btn' onClick={this.props.dec}>-</Button>
  29. <Button className='dec_btn' onClick={this.props.asyncAdd}>async</Button>
  30. <View>{this.props.counter.num}</View>
  31. </View>
  32. )
  33. }
  34. }
  35. export default Index

connect 方法接受兩個(gè)參數(shù) mapStateToPropsmapDispatchToProps

  • mapStateToProps,函數(shù)類(lèi)型,接受最新的 state 作為參數(shù),用于將 state 映射到組件的 props
  • mapDispatchToProps,函數(shù)類(lèi)型,接收 dispatch() 方法并返回期望注入到展示組件的 props 中的回調(diào)方法

Hooks

在 Redux 中使用 Hooks

使用 hooks 的基本設(shè)置和使用 connect 的設(shè)置是一樣的,你需要設(shè)置你的 store,并把你的應(yīng)用放在 Provider 組件中。

  1. const store = configreStore(rootReducer)
  2. class App extends Components {
  3. render () {
  4. return (
  5. <Provider store={store}>
  6. <Index />
  7. </Provider>
  8. )
  9. }
  10. }

在這樣的情況下,你就可以使用 taro-redux 提供的 Hooks API 在函數(shù)式組件中使用。

useSelector

  1. const result : any = useSelector(selector : Function, equalityFn? : Function)

useSelector 允許你使用 selector 函數(shù)從一個(gè) Redux Store 中獲取數(shù)據(jù)。

Selector 函數(shù)大致相當(dāng)于 connect 函數(shù)的 mapStateToProps 參數(shù)。Selector 會(huì)在組件每次渲染時(shí)調(diào)用。useSelector 同樣會(huì)訂閱 Redux store,在 Redux action 被 dispatch 時(shí)調(diào)用。

useSelector 還是和 mapStateToProps 有一些不同:

  • 不像 mapStateToProps 只返回對(duì)象一樣,Selector 可能會(huì)返回任何值。
  • 當(dāng)一個(gè) action dispatch 時(shí),useSelector 會(huì)把 selector 的前后返回值做一次淺對(duì)比,如果不同,組件會(huì)強(qiáng)制更新。
  • Selector 函數(shù)不接受 ownProps 參數(shù)。但 selector 可以通過(guò)閉包訪問(wèn)函數(shù)式組件傳遞下來(lái)的 props。

使用案例

基本使用:

  1. import Taro, { Components } from '@tarojs/taro'
  2. import { useSelector } from '@tarojs/redux'
  3. export const CounterComponent = () => {
  4. const counter = useSelector(state => state.counter)
  5. return <View>{counter}</View>
  6. }

使用閉包決定如何 select 數(shù)據(jù):

  1. export const TodoListItem = props => {
  2. const todo = useSelector(state => state.todos[props.id])
  3. return <View>{todo.text}</View>
  4. }

進(jìn)階使用

你還可以訪問(wèn) react-redux 文檔 了解如何使用 reselect 緩存 selector。

useDispatch

  1. const dispatch = useDispatch()

這個(gè) Hook 返回 Redux store 的 dispatch 引用。你可以使用它來(lái) dispatch actions。

使用案例

  1. import Taro, { Components } from '@tarojs/taro'
  2. import { useDispatch } from '@tarojs/redux'
  3. export const CounterComponent = ({ value }) => {
  4. const dispatch = useDispatch()
  5. return (
  6. <View>
  7. <Text>{value}</Text>
  8. <Button onClick={() => dispatch({ type: 'increment-counter' })}>
  9. Increment counter
  10. </Button>
  11. </View>
  12. )
  13. }

當(dāng)我們使用 dispatch 傳遞回調(diào)到一個(gè)子組件時(shí),推薦使用 useCallback 把回調(diào)緩存起來(lái),因?yàn)榻M件可能因?yàn)橐酶淖兌匦落秩尽?/p>

  1. // CounterComponent.js
  2. export const CounterComponent = ({ value }) => {
  3. const dispatch = useDispatch()
  4. const incrementCounter = useCallback(
  5. () => dispatch({ type: 'increment-counter' }),
  6. [dispatch]
  7. )
  8. return (
  9. <View>
  10. <Text>{value}</Text>
  11. <MyIncrementButton onIncrement={incrementCounter} />
  12. </View>
  13. )
  14. }
  15. // IncrementButton.js
  16. const MyIncrementButton = ({ onIncrement }) => (
  17. <Button onClick={onIncrement}>Increment counter</Button>
  18. )
  19. export default Taro.memo(MyIncrementButton)

useStore

  1. const store = useStore()

useStore 返回一個(gè) store 引用和 Provider 組件引用完全一致。

這個(gè) hook 可能并不經(jīng)常使用。useSelector 大部分情況是你的第一選擇,如果需要替換 reducers 的情況下可能會(huì)使用到這個(gè) API。

使用案例

  1. import Taro, { Components } from '@tarojs/taro'
  2. import { useStore } from '@tarojs/redux'
  3. export const CounterComponent = ({ value }) => {
  4. const store = useStore()
  5. // EXAMPLE ONLY! Do not do this in a real app.
  6. // The component will not automatically update if the store state changes
  7. return <div>{store.getState()}</div>
  8. }


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)