title: Use Redux

You can freedomly use Redux which is a very popular tool in React community to solve data management problems for complex projects.

First please install reduxreact-reduxredux-thunk and redux-logger and so on ,such as redux middleware which you need.

  1. $ yarn add redux react-redux redux-thunk redux-logger
  2. # or use npm
  3. $ npm install --save redux react-redux redux-thunk redux-logger

Then you can create a new directory named store under the project src directory, and add an index.js file under the directory to configure the store, set up the middleware of redux according to your preferences. As in the following example, using in the following example redux-thunk and redux-logger these two middleware.

```jsx title=”src/store/index.js” import { createStore, applyMiddleware, compose } from ‘redux’ import thunkMiddleware from ‘redux-thunk’ import rootReducer from ‘../reducers’

const composeEnhancers = typeof window === ‘object’ && window.REDUX_DEVTOOLS_EXTENSION_COMPOSE ?
window.REDUX_DEVTOOLS_EXTENSION_COMPOSE({ // Specify extension’s options like name, actionsBlacklist, actionsCreators, serialize… }) : compose

const middlewares = [ thunkMiddleware ]

if (process.env.NODE_ENV === ‘development’ && process.env.TARO_ENV !== ‘quickapp’) { middlewares.push(require(‘redux-logger’).createLogger()) }

const enhancer = composeEnhancers( applyMiddleware(…middlewares), // other store enhancers if any )

export default function configStore () { const store = createStore(rootReducer, enhancer) return store }

  1. Next, use the `Provider` component provided in `redux` to connect the previously written `store` to the application, in the project entry file `app.js`.
  2. ```jsx title="src/app.js"
  3. import React, { Component } from 'react'
  4. import { Provider } from 'react-redux'
  5. import configStore from './store'
  6. import './app.css'
  7. const store = configStore()
  8. class App extends Component {
  9. componentDidMount () {}
  10. componentDidShow () {}
  11. componentDidHide () {}
  12. componentDidCatchError () {}
  13. // The render() function has no practical effect in the App class.
  14. // Please don't modify the function!
  15. render () {
  16. return (
  17. <Provider store={store}>
  18. {this.props.children}
  19. </Provider>
  20. )
  21. }
  22. }
  23. export default App

Then you can start use it. As recommended by redux, you can add

-constants directory, used to store all action type constants -actions directory, used to store all actions -reducers directory, used to store all reducers

For example, If we want to develop a simple counter function just contains add and subtract.

Add action type

```jsx title=”src/constants/counter.js” export const ADD = ‘ADD’ export const MINUS = ‘MINUS’

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

```jsx title=”src/reducers/index.js” import { combineReducers } from ‘redux’ import counter from ‘./counter’

export default combineReducers({ counter })

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

Finally, we can use it in the page (or component).The connect method provided by redux will commect redux with our page.

```jsx title=”src/pages/index/index.js” import React, { Component } from ‘react’ import { connect } from ‘react-redux’ import { View, Button, Text } from ‘@tarojs/components’

import { add, minus, asyncAdd } from ‘../../actions/counter’

import ‘./index.css’

@connect(({ counter }) => ({ counter }), (dispatch) => ({ add () { dispatch(add()) }, dec () { dispatch(minus()) }, asyncAdd () { dispatch(asyncAdd()) } })) class Index extends Component { componentWillReceiveProps (nextProps) { console.log(this.props, nextProps) }

componentWillUnmount () { }

componentDidShow () { }

componentDidHide () { }

render () { return ( {this.props.counter.num} Hello, World ) } }

export default Index

  1. `connect` method will receive two parameters: `mapStateToProps` and `mapDispatchToProps`.
  2. - `mapStateToProps`, function type, accepts the latest `state` as a parameter, which is used to map `state` to component `props`.
  3. - `mapDispatchToProps`, function type, receive the `dispatch()` method and return the callback function expected to be injected into the `props` of the display component.
  4. ## Hooks
  5. ### Use Hooks in Redux
  6. The basic setting of using hooks is the same as `connect`. You need to set up your `store` and put your application in the `Provider` component.
  7. 使用 hooks 的基本设置和使用 `connect` 的设置是一样的, 你需要设置你的 `store`, 并把你的应用放在 `Provider` 组件中。
  8. ```jsx
  9. const store = configreStore(rootReducer)
  10. class App extends Components {
  11. render () {
  12. return (
  13. <Provider store={store}>
  14. <Index />
  15. </Provider>
  16. )
  17. }
  18. }

In this case, you can use Hooks API provided by redux in function component.

useSelector

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

useSelector allows you to use selector function to get data from a Redux Store.

The Selector funtion is roughly equivalent to the mapStateToProps parameter of the connect function. It will be called every time the component renders. And it will also subscribe to the Redux store, which will be called when a Redux action is dispatched.

But useSelector is still somewhat different from mapStateToProps:

  • Unlike mapStateToProps which only returns objects, the Selector may return any value.
  • When an action is dispatched, useSelector will make a shallow comparison of the return value before and after the selector. If they are different, the component will be forced to update.
  • The Selector function does not accept the ownProps parameter. But selector can access the props passed down by functional components through closures

Use Case

Basic usage:

  1. import React, { Component } from 'react'
  2. import { useSelector } from 'react-redux'
  3. export const CounterComponent = () => {
  4. const counter = useSelector(state => state.counter)
  5. return <View>{counter}</View>
  6. }

Use the closure to decide how to select data:

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

Advanced Usage

You could learn how to use reselect to cache selector from react-redux documentatio.

useDispatch

  1. const dispatch = useDispatch()

This Hook will return a reference to the dispatch of the Redux store. You can use it to dispatch actions.

Use Case

  1. import React, { Component } from 'react'
  2. import { useDispatch } from 'react-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. }

It’s recommend to use useCallback to cache the callback, when we use dispatch to pass a callback to child component. Because the component may be re-redered due to changes in references.

  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 returns a store reference, which is exactly the same as the Provider component reference.

This hook may not be used often, But useSelector is your first choice in most cases, If you need to replace reducers, you may use this API.

Use case

  1. import React, { Component } from 'react'
  2. import { useStore } from 'react-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. }