redux

redux

記錄一下 redux 的一些用法,若是想學習 redux,建議看官方文檔,另外推薦一本huzidaha寫的react小書,裏面講解了一些 react 和 redux 的原理。javascript

start


運行以下命令,不瞭解 npx 的,能夠看一下阮一峯的文章html

// 腳手架建立項目
npx create-react-app redux-test
// 進入文件夾
cd redux-test
// 啓動 react
npm run start

而後安裝 redux:java

npm i -S redux react-redux

接着把 src 下的文件都刪掉幾個,只剩兩個文件:react

src/
|--index.js
|--serviceWorker.js

redux and react-redux


打開上面留下的 index.js,刪掉裏面的代碼,敲下本身的代碼,而後刷新網頁。git

// index.js
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import * as serviceWorker from './serviceWorker'
import { createStore } from 'redux'
import { connect, Provider  } from 'react-redux'

// actionTypes
const NUM_ADD = 'NUM_ADD';

// actionCreator
function addNum () {
  return {
    type: NUM_ADD
  }
}

// reducer
const initialState = {
  num: 0
}

function counter (state = initialState, action) {
  switch  (action.type) {
    case NUM_ADD:
      return {
        num: state.num + 1
      }
    default:
      return state
  }
}

// store
const store = createStore(counter)

// App
class App extends Component {
  render() {
    return (
      <div>
        {this.props.num}
        <button onClick={this.props.onClickAdd}>add</button>
      </div>
    );
  }
}

// connect(mapStateToProps, mapDispatchToProps)(component)
const mapStateToProps = state => {
  return {
    num: state.num
  }
}

const mapDispatchToProps = dispatch => {
  return {
    onClickAdd: () => {
      dispatch(addNum())
    }
  }
}

App = connect(mapStateToProps, mapDispatchToProps)(App)

// Provider 傳入 store
ReactDOM.render(
<Provider store={store}>
  <App />
</Provider>, document.getElementById('root'));

serviceWorker.unregister();

mapDispatchToProps


connect中的mapDispatchToProps能夠是一個函數,也能夠是一個對象,函數寫法如上。github

mapDispatchToProps是一個對象時,redux 會把它裏面的屬性做爲actionCreator交給dispatch使用,簡單來講,就是 redux 幫你把對象裏面的屬性封裝爲函數型的mapDispatchToProps,寫法以下:shell

// index.js
...
// App
class App extends Component {
  render() {
    return (
      <div>
        {this.props.num}
        // 這裏也進行了修改 
        // onClickAdd -> addNum
        <button onClick={this.props.addNum}>add</button>
      </div>
    );
  }
}
...
// connect
App = connect( 
  state => state, 
  { addNum }
)(App)
...

@connect


connect可使用裝飾器的寫法。npm

// 裝飾器的 babel 插件
npm i -S @babel/plugin-proposal-decorators

而後進行配置 plugin,這裏有兩種配置方法:json

  1. 使用 create-react-app 的配置redux

    // 暴露配置
    npm run eject
    // 會進行確認
    Are you sure you want to eject? This action is permanent.(y/n) y

    而後會看到文件夾內多了一些東西,打開項目根路徑下的 package.json 文件,找到 babel 配置項:

    // package.json
    ...
    "babel": {
        "presets": [
          "react-app"
        ],
        "plugins": [["@babel/plugin-proposal-decorators", { "legacy": true }]]
      },
    ...
  2. 使用獨立文件配置 babel:

    打開項目根路徑下的 package.json 文件,找到 babel 配置項,將他刪掉:

    // package.json
    ...
    // 把這個刪掉
    "babel": {
        "presets": [
          "react-app"
        ],
      },
    ...

    而後在項目根目錄建立.babelrc文件。

    // .babelrc
    {
      "presets": ["react-app"],
      "plugins": [
        ["@babel/plugin-proposal-decorators", { "legacy": true }]
    }

配置完 babel 以後,打開 src 下的 index.js 進行修改:

// index.js
...
// App
// 裝飾器寫法
@connect(
  state => state, 
  { addNum }
)
class App extends Component {
  render() {
    return (
      <div>
        {this.props.num}
        <button onClick={this.props.addNum}>add</button>
      </div>
    );
  }
}
...

注意:對於裝飾器的支持只是實驗性的,將來可能會改動。

redux-thunk


可使用中間件 redux-thunk 進行異步操做,它可讓actionCreator不返回action對象,而是返回一個函數,能夠在函數內封裝邏輯。

function incrementIfOdd() {
  // 接收兩個參數
  // getState() 能夠拿到 store 中的 state
  return (dispatch, getState) => {
    const { counter } = getState();
    if (counter % 2 === 0) {
      return;
    }
    dispatch(increment());
  };
}

首先仍是安裝:

npm i -S redux-thunk

而後打開 index.js 進行修改:

// index.js
...
import {createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
...
// store
const store = createStore(counter, applyMiddleware(thunk))
...

異步操做有不少,這裏將 num 的增長推遲爲 1s 後才進行:

// index.js
// actionCreator
function addNum () {
  return {
    type: NUM_ADD
  }
}

function addNumAsync () {
  return dispatch => {
    setTimeout(() => {
      dispatch({type: NUM_ADD})
    }, 1000)
  }
}

// App
@connect(
  state => state, 
  { addNum, addNumAsync }
)
class App extends Component {
  render() {
    return (
      <div>
        {this.props.num}
        <button onClick={this.props.addNum}>add</button>
        <button onClick={this.props.addNumAsync}>add after 1s</button>
      </div>
    );
  }
}

combineReducers


當你有多個 reducer 時,可使用combineReducers,進行合併。

import { createStore, combineReducers } from 'redux'

const allReducer = combineReducers({
  reducerOne,
  reducersTwo
})

store = createStore(allReducer)

bindActionCreators


把 actionCreator 傳到一個子組件中,卻不想讓這個組件覺察到 Redux 的存在,並且不但願把 dispatch 或 store 傳給它時,可使用bindActionCreators

這裏使用官方文檔的例子:

// TodoActionCreators.js
export function addTodo(text) {
  return {
    type: 'ADD_TODO',
    text
  };
}

export function removeTodo(id) {
  return {
    type: 'REMOVE_TODO',
    id
  };
}
// SomeComponent.js
import { Component } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';

import * as TodoActionCreators from './TodoActionCreators';
console.log(TodoActionCreators);
// {
//   addTodo: Function,
//   removeTodo: Function
// }

class TodoListContainer extends Component {
  constructor(props) { 
    super(props);

    const {dispatch} = props;

    // 這是一個很好的 bindActionCreators 的使用示例:
    // 你想讓你的子組件徹底不感知 Redux 的存在。
    // 咱們在這裏對 action creator 綁定 dispatch 方法,
    // 以便稍後將其傳給子組件。

    this.boundActionCreators = bindActionCreators(TodoActionCreators, dispatch);
    console.log(this.boundActionCreators);
    // {
    //   addTodo: Function,
    //   removeTodo: Function
    // }
  }

  componentDidMount() {
    // 由 react-redux 注入的 dispatch:
    let { dispatch } = this.props;

    // 注意:這樣是行不通的:
    // TodoActionCreators.addTodo('Use Redux')

    // 你只是調用了建立 action 的方法。
    // 你必需要同時 dispatch action。

    // 這樣作是可行的:
    let action = TodoActionCreators.addTodo('Use Redux');
    dispatch(action);
  }

  render() {
    // 由 react-redux 注入的 todos:
    let { todos } = this.props;

    return <TodoList todos={todos} {...this.boundActionCreators} />;

    // 另外一替代 bindActionCreators 的作法是
    // 直接把 dispatch 函數看成 prop 傳遞給子組件,但這時你的子組件須要
    // 引入 action creator 而且感知它們

    // return <TodoList todos={todos} dispatch={dispatch} />;
  }
}

export default connect(state => ({ todos: state.todos }))(TodoListContainer)

備註


我的學習 redux 的感覺,看,不如動手去敲。

相關文章
相關標籤/搜索