1. 程式人生 > 實用技巧 >taro使用 Redux官方教程

taro使用 Redux官方教程

在 Taro 中可以自由地使用React生態中非常流行的資料流管理工具Redux來解決複雜專案的資料管理問題。而為了更方便地使用Redux,Taro 提供了與react-reduxAPI 幾乎一致的包@tarojs/redux來讓開發人員獲得更加良好的開發體驗。

下文中示例程式碼均在taro-redux-sample

首先請安裝redux@tarojs/redux@tarojs/redux-h5,以及一些需要用到的redux中介軟體

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

隨後可以在專案src目錄下新增一個store目錄,在目錄下增加index.js檔案用來配置store,按自己喜好設定redux的中介軟體,例如下面例子中使用redux-thunkredux-logger這兩個中介軟體

// src/store/index.js import { createStore, applyMiddleware } from 'redux' import thunkMiddleware from 'redux-thunk'
import { createLogger } from 'redux-logger' import rootReducer from '../reducers' const middlewares = [ thunkMiddleware, createLogger() ] export default function configStore () { const store = createStore(rootReducer, applyMiddleware(...middlewares)) return store }

接下來在專案入口檔案app.js中使用@tarojs/redux

中提供的Provider元件將前面寫好的store接入應用中

// src/app.js import Taro, { Component } from '@tarojs/taro' import { Provider } from '@tarojs/redux' import configStore from './store' import Index from './pages/index' import './app.scss' const store = configStore() class App extends Component { render() { return ( <Provider store={store}> {this.props.children} </Provider> ) } } Taro.render(<App />, document.getElementById('app'))

然後就可以開始使用了。如redux推薦的那樣,可以增加

  • constants目錄,用來放置所有的action type常量
  • actions目錄,用來放置所有的actions
  • reducers目錄,用來放置所有的reducers

例如我們要開發一個簡單的加、減計數器功能

新增action type

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

新增reducer處理

// src/reducers/counter.js import { ADD, MINUS } from '../constants/counter' const INITIAL_STATE = { num: 0 } export default function counter (state = INITIAL_STATE, action) { switch (action.type) { case ADD: return { ...state, num: state.num + 1 } case MINUS: return { ...state, num: state.num - 1 } default: return state } } // src/reducers/index.js import { combineReducers } from 'redux' import counter from './counter' export default combineReducers({ counter })

新增action處理

// src/actions/counter.js import { ADD, MINUS } from '../constants/counter' export const add = () => { return { type: ADD } } export const minus = () => { return { type: MINUS } } // 非同步的 action export function asyncAdd () { return dispatch => { setTimeout(() => { dispatch(add()) }, 2000) } }

最後,我們可以在頁面(或者元件)中進行使用,我們將通過tarojs/redux提供的connect方法將redux與我們的頁面進行連線

// src/pages/index/index.js import Taro, { Component } from '@tarojs/taro' import { View, Text } from '@tarojs/components' import { connect } from '@tarojs/redux' import './index.scss' import { add, minus, asyncAdd } from '../../actions/counter' @connect(({ counter }) => ({ counter }), (dispatch) => ({ add () { dispatch(add()) }, dec () { dispatch(minus()) }, asyncAdd () { dispatch(asyncAdd()) } })) class Index extends Component { config = { navigationBarTitleText: '首頁' } render () { return ( <View className='todo'> <Button className='add_btn' onClick={this.props.add}>+</Button> <Button className='dec_btn' onClick={this.props.dec}>-</Button> <Button className='dec_btn' onClick={this.props.asyncAdd}>async</Button> <View>{this.props.counter.num}</View> </View> ) } } export default Index

connect方法接受兩個引數mapStateToPropsmapDispatchToProps

  • mapStateToProps,函式型別,接受最新的state作為引數,用於將state對映到元件的props
  • mapDispatchToProps,函式型別,接收dispatch()方法並返回期望注入到展示元件的props中的回撥方法

#Hooks

#在 Redux 中使用 Hooks

使用 hooks 的基本設定和使用connect的設定是一樣的,你需要設定你的store,並把你的應用放在Provider元件中。

const store = configreStore(rootReducer) class App extends Components { render () { return ( <Provider store={store}> <Index /> </Provider> ) } }

在這樣的情況下,你就可以使用taro-redux提供的 Hooks API 在函式式元件中使用。

#useSelector

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

useSelector允許你使用 selector 函式從一個 Redux Store 中獲取資料。

Selector 函式大致相當於connect函式的mapStateToProps引數。Selector 會在元件每次渲染時呼叫。useSelector同樣會訂閱 Redux store,在 Redux action 被 dispatch 時呼叫。

useSelector還是和mapStateToProps有一些不同:

  • 不像mapStateToProps只返回物件一樣,Selector 可能會返回任何值。
  • 當一個 action dispatch 時,useSelector會把 selector 的前後返回值做一次淺對比,如果不同,元件會強制更新。
  • Selector 函式不接受ownProps引數。但 selector 可以通過閉包訪問函式式元件傳遞下來的 props。

#使用案例

基本使用:

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

使用閉包決定如何 select 資料:

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

#進階使用

你還可以訪問react-redux 文件瞭解如何使用reselect快取 selector。

#useDispatch

const dispatch = useDispatch()

這個 Hook 返回 Redux store 的dispatch引用。你可以使用它來 dispatch actions。

#使用案例

import Taro, { Components } from '@tarojs/taro' import { useDispatch } from '@tarojs/redux' export const CounterComponent = ({ value }) => { const dispatch = useDispatch() return ( <View> <Text>{value}</Text> <Button onClick={() => dispatch({ type: 'increment-counter' })}> Increment counter </Button> </View> ) }

當我們使用dispatch傳遞迴調到一個子元件時,推薦使用useCallback把回撥快取起來,因為元件可能因為引用改變而重新渲染。

// CounterComponent.js export const CounterComponent = ({ value }) => { const dispatch = useDispatch() const incrementCounter = useCallback( () => dispatch({ type: 'increment-counter' }), [dispatch] ) return ( <View> <Text>{value}</Text> <MyIncrementButton onIncrement={incrementCounter} /> </View> ) } // IncrementButton.js const MyIncrementButton = ({ onIncrement }) => ( <Button onClick={onIncrement}>Increment counter</Button> ) export default Taro.memo(MyIncrementButton)

#useStore

const store = useStore()

useStore返回一個 store 引用和Provider元件引用完全一致。

這個 hook 可能並不經常使用。useSelector大部分情況是你的第一選擇,如果需要替換 reducers 的情況下可能會使用到這個 API。

#使用案例

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