在 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
中間件
$ 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
隨后可以在項(xiàng)目 src
目錄下新增一個(gè) store
目錄,在目錄下增加 index.js
文件用來(lái)配置 store
,按自己喜好設(shè)置 redux
的中間件,例如下面例子中使用 redux-thunk
和 redux-logger
這兩個(gè)中間件
// 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
}
接下來(lái)在項(xiàng)目入口文件 app.js
中使用 @tarojs/redux
中提供的 Provider
組件將前面寫(xiě)好的 store
接入應(yīng)用中
// 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'))
然后就可以開(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
// 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)
}
}
最后,我們可以在頁(yè)面(或者組件)中進(jìn)行使用,我們將通過(guò) tarojs/redux
提供的 connect
方法將 redux
與我們的頁(yè)面進(jìn)行連接
// 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: '首頁(yè)'
}
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
方法接受兩個(gè)參數(shù) mapStateToProps
與 mapDispatchToProps
mapStateToProps
,函數(shù)類(lèi)型,接受最新的 state
作為參數(shù),用于將 state
映射到組件的 props
mapDispatchToProps
,函數(shù)類(lèi)型,接收 dispatch()
方法并返回期望注入到展示組件的 props
中的回調(diào)方法使用 hooks 的基本設(shè)置和使用 connect
的設(shè)置是一樣的,你需要設(shè)置你的 store
,并把你的應(yīng)用放在 Provider
組件中。
const store = configreStore(rootReducer)
class App extends Components {
render () {
return (
<Provider store={store}>
<Index />
</Provider>
)
}
}
在這樣的情況下,你就可以使用 taro-redux
提供的 Hooks API 在函數(shù)式組件中使用。
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ì)返回任何值。useSelector
會(huì)把 selector 的前后返回值做一次淺對(duì)比,如果不同,組件會(huì)強(qiáng)制更新。ownProps
參數(shù)。但 selector 可以通過(guò)閉包訪問(wèn)函數(shù)式組件傳遞下來(lái)的 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 數(shù)據(jù):
export const TodoListItem = props => {
const todo = useSelector(state => state.todos[props.id])
return <View>{todo.text}</View>
}
你還可以訪問(wèn) react-redux 文檔 了解如何使用 reselect
緩存 selector。
const dispatch = useDispatch()
這個(gè) Hook 返回 Redux store 的 dispatch
引用。你可以使用它來(lái) 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>
)
}
當(dāng)我們使用 dispatch
傳遞回調(diào)到一個(gè)子組件時(shí),推薦使用 useCallback
把回調(diào)緩存起來(lái),因?yàn)榻M件可能因?yàn)橐酶淖兌匦落秩尽?/p>
// 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)
const store = useStore()
useStore
返回一個(gè) store 引用和 Provider
組件引用完全一致。
這個(gè) hook 可能并不經(jīng)常使用。useSelector
大部分情況是你的第一選擇,如果需要替換 reducers 的情況下可能會(huì)使用到這個(gè) 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>
}
更多建議: