Redux學習筆記

什麼是Redux

Redux 是 JavaScript 狀態容器, 提供可預測化的狀態管理。它專門爲React.js這門框架設計,但並非只能用於react,能夠用於任何界面庫。javascript

Redux的設計原則

Redux在設計與實現時,遵循三大原則或者說規範限制css

單一數據源

整個應用程序的轉態數據僅存儲子一個Store中,數據java

只讀的 State

惟一的 Store 也是隻讀的,應用程序沒法直接修改狀態數據。Store 對象自己的 API 很是少,僅有四個方法:react

  • getState():獲取當前狀態數據
  • dispatch(action):推送觸發變化
  • subscribe(listener):訂閱數據變化
  • replaceReducer(nextReducer)

顯而易見,沒有提供設置狀態的方法。其中的 dispatch(action) 是惟一改變數據的方法,好比:redux

var action = {
  type: 'ADD_USER',
  user: {name: 'chuonye.com'}
};
store.dispatch(action);
複製代碼

dispatch 方法會將 action 傳遞給 Reduxaction 就是一個普通對象,包含觸發的操做類型以及數據。api

使用純函數執行狀態的更新

Redux 接收到 action 後,會使用一個純函數來處理,這些函數被稱爲 Reducers:markdown

var someReducer = function(state, action) {
  ...
  return state;
}
複製代碼

Reducer 接收當前的 state 和 action 做爲參數,它也不能直接修改原有的數據,而是經過返回一個新的 state 來修改。總的來講,Redux 是一個幫助應用統一管理狀態數據的工具,它遵循嚴格的單向數據流(Store 只讀)設計,使得應用的行爲變得可預測且容易理解。antd

image.png

使用Redux的基本流程

在介紹使用Redux的基本使用流程前,咱們基於原生的方法實現TodoList的案例,在此基礎之上分別演示Redux和React-Redux的使用流程app

原生React的實現TodoList

示例:框架

import React, { Component } from 'react';
import {Row,Col,Button,Divider,Input,List,} from 'antd';

class TodoList extends Component {

    constructor(props) {
        super(props)
        this.state = {
            todos: ["籃球", "足球", "羽毛球"],
            inpVal: ""
        }
    }

    handleChange = (e) => {
        this.setState({ inpVal: e.target.value })
    }

    handleClick = () => {
        const { inpVal } = this.state
        let todos = this.state.todos
        todos.push(inpVal)
        this.setState({ todos })

    }

    handleDelete = (id) => {
        let {todos} = this.state
        todos = todos.filter((v,index) => index != id)
        this.setState({todos})
    }

    render() {
        return (
            <div> <h3>React原生實現TodoList</h3> <Divider /> <Input onChange={this.handleChange} placeholder="請輸入" ></Input> <Button onClick={this.handleClick}>添加</Button> <Divider /> <Row> <Col span={6}> <List bordered dataSource={this.state.todos} renderItem={(item, index) => ( <List.Item extra={<a onClick={this.handleDelete.bind(this,index)}>刪除</a>} >{item}</List.Item>)} > </List> </Col> </Row> </div>
        )
    }
}

export default TodoList
複製代碼

基於Redux實現TodoList

安裝 Redux

yarn add redux --save
複製代碼

基於Redux實現TodoList的具體工做步驟:

  • 建立 Action
    • 根目錄下建立 action 文件夾並在文件夾下建立 index.jsx
    • 構建action函數用來返回 action 對象,注意 action 必須包含 type 屬性
    • action 並導出

示例:

// src/action/index.jsx
// 建立Todo的Action
const addTodoList = (iptVal) => ({ type: 'ADD', iptVal })
// 查詢當前的TodoList
const seleteTodoList = () => ({ type: 'SELECT' })
// 監聽Input
const InputChange = (iptVal) => ({ type: 'INPUT', iptVal })
// 刪除Todo的Action
const DeleteTodoList = (index) => ({ type: 'DELETE', index })

module.exports = {
    addTodoList,seleteTodoList,InputChange,DeleteTodoList
}
複製代碼
  • 建立 Reducer
    • 根目錄下建立 reducer 文件夾並在文件夾下建立 index.jsx
    • 構建 reducer,注意 reducer 要接收兩個參數
    • 第一個參數是默認狀態,定義一個初始化的 state,而後進行賦值

示例:

/src/reducer/index.jsx
// 1. 初始化
const initState = {
    todos: ["籃球", "足球", "羽毛球"],
    iptVal: ""
}
// 2. 建立reducer
const reducer = (state = initState, action) => {
    let newState = Object.assign({}, state, action);
    switch (action.type) {
        case "ADD":
            const { iptVal, todos } = state
            newState.todos.push(iptVal)
            return newState
        case 'SELECT':
            return Object.assign({}, state, action)
        case 'INPUT':
            return Object.assign({}, state, action)
        case 'DELETE':
            const { index } = action
            newState.todos.splice(index, 1)
            return newState
        default:
            return state;
    }
}

export default reducer
複製代碼
  • 建立 Store
    • 跟目錄下建立 store 的文件夾而且在文件夾下建立 index.jsx
    • 使用 createStore 函數裏面第一個參數接收的是 reducer
    • 導入 reducer 設置到函數中 createStore(reducer)
    • 將建立的 store 進行導出

示例:

import { createStore } from 'redux'

import reducer from './../reducer/'

const store = createStore(reducer)

export default store
複製代碼
  • 組件中使用Redux
    • 綁定按鈕監聽事件
    • 在組件加載完畢的時候經過 store 進行監聽器的註冊,返回值能夠用來監聽註冊
    • 經過 store.dispatch(action) 發送action

示例:

import React, { Component } from 'react';
import {
    Divider,
    Input,
    Button,
    Row,
    Col,
    List
} from 'antd'
import store from '../../store/index'

import { 
    addTodoList, 
    seleteTodoList, 
    InputChange, 
    DeleteTodoList 
} from './../../action/index'


class Home extends Component {

    constructor(props) {
        super(props)
        this.state = store.getState()
    }

    componentWillMount() {
        const action = seleteTodoList()
        // 發送action
        store.dispatch(action)
    }

    handleChange = (e) => {
        const action = InputChange(e.target.value)
        // 發送action
        store.dispatch(action)
    }

    handleClick = () => {
        const { iptVal } = this.state
        const action = addTodoList(iptVal)
        // 發送action
        store.dispatch(action)
    }

    handleDelete = (index) => {
        const action = DeleteTodoList(index)
        // 發送action
        store.dispatch(action)
    }

    componentDidMount() {
        store.subscribe(() => this.setState(store.getState()))
    }

    render() {
        return (
            <div style={{ width: 500, height: 500, textAlign: 'center', margin: '0 auto' }} > <h3>Redux實現TodoList</h3> <Divider /> <Input onChange={this.handleChange} placeholder="請輸入" style={{ width: 300, marginRight: 50 }} value={this.state.iptVal} ></Input> <Button onClick={this.handleClick}>添加</Button> <Divider /> <Row> <Col> <List bordered dataSource={this.state.todos} renderItem={(item, index) => ( <List.Item extra={<span onClick={this.handleDelete.bind(this, index)}>刪除</span>} >{item}</List.Item>)} > </List> </Col> </Row> </div>
        )
    }
}

export default Home;
複製代碼

Redux調用流程的總結

基本的使用步驟:

  1. 建立action
  2. 發送 actionreducer -- store.dispatch(action)
  3. reducer(preState,action)進行業務邏輯的處理返回store
  4. 監聽 store 的變化,基於store 提供的api store.getState()獲取最新的state
action --> store.dispatch(action) --> reducer(action) --> store
複製代碼

基於React-Redux實現TodoList

建立項目

create-react-app demo02
cd demo02
yarn add redux --save
yarn add react-redux --save
yarn add antd@"^3.26.20" --save
yarn add react@"^16.14.0" --save
複製代碼

實現步驟

  1. 基於 redux 構建 store,在 src 下建立 store 目錄並建立 index.jsx 文件
  2. 建立 reducer/index.jsx 文件,構建 reducer 響應 action
  3. 經過 createStorereducer 注入返回 store
  4. src/App.js 引入 store 並導入 Provider 將整個結構進行包裹,而且傳遞 store
  5. 引入 connect 對組件進行增強,mapDispatchToProps 返回的是一個對象 {key:方法名,value:調用dispatch發送action},在組件中經過 this.props 能夠獲取到該對象 。connect 函數的基本介紹:
參數名 類型 說明
mapStateToProps(state,ownProps) Function 該函數將 store 中的數據做爲 props 綁定到到組件
state:redux 中的 store
ownProps:組件中的 props
mapDispatchToProps(dispatch,ownProps) Function action 做爲 props 綁定到組件中
dispatch:就是 store.dispatch()
ownProps:組件中的props
mergeProps(stateProps,dispachProps,ownProps) Function 無論是stateProps,dispachProps 都須要和ownProps 合併之後賦給組件,一般狀況下能夠不傳遞這個參數connect會使用Object.assign替代該方法
options Object 定製 connector 的行爲

構建 reducer

// src/reducer/index.jsx

const initState = {
  todos: ["籃球", "足球", "羽毛球"],
}

const rootReducer = (state = initState, action) => {

  if (action.type === "ADD") {
    const { iptVal } = action
    let newState = JSON.parse(JSON.stringify(state))
    newState.todos.push(iptVal)
    return newState
  } else if (action.type === 'SELECT') {
    let newState = JSON.parse(JSON.stringify(state))
    return newState
  } else if (action.type === "DELETE") {
    const { index } = action
    let newState = JSON.parse(JSON.stringify(state))
    newState.todos.splice(index, 1)
    return newState
  } else {
    return state
  }
}

export default rootReducer
複製代碼

構建 store

// src/store/index.jsx
import { createStore } from "redux";
import reducer from "../reducer";
const store = createStore(reducer)
export default store
複製代碼

導入Provider傳遞store

// App.jsx
import React, {
  Component
} from 'react';

import {
  Provider
} from 'react-redux'

import store from './store';
import AddTodo from './pages/AddTodo';
import TodoList from './pages/TodoList'
import './App.css';


class App extends Component {

  render() {
    return (
      <div style={{ width: 500, height: 500, textAlign: 'center', margin: '0 auto' }}> <Provider store={store}> <h3>React-Reudx實現TodoList</h3> <AddTodo /> <br /> <TodoList /> </Provider> </div>
    );
  }
}

export default App;
複製代碼

AddTodo 組件

import React, { Component } from 'react';
import {
  Input,
  Button,
} from 'antd'

import { connect } from 'react-redux'

class AddTodo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      iptVal: ""
    }
  }

  handleClick = () => {
    const { iptVal } = this.state
    this.props.AddAction(iptVal)
  }

  render() {
    return (
      <div> <Input onChange={(e) => this.setState({ iptVal: e.target.value })} placeholder="請輸入" style={{ width: 300, marginRight: 50 }} value={this.state.iptVal} ></Input> <Button onClick={this.handleClick}>添加</Button> </div>
    );
  }
}


const mapDispatchToProps = (dispatch) => {
  return {
    AddAction: (iptVal) => {dispatch({type: 'ADD',iptVal})}
  }
}

export default connect(null, mapDispatchToProps)(AddTodo);

複製代碼

TodoList 組件

import React, { Component } from 'react';
import {
  Row,
  Col,
  List
} from 'antd'
import { connect } from 'react-redux'
class TodoList extends Component {

  handleDelete = (index) => {
    this.props.RemoveAction(index)
  }

  render() {
    return (
      <div> <Row> <Col> <List bordered dataSource={this.props.todos} renderItem={(item, index) => ( <List.Item extra={<span onClick={this.handleDelete.bind(this, index)}>刪除</span>} >{item}</List.Item>)} > </List> </Col> </Row> </div>
    );
  }
}

const mapStateToProps = (state) => {
  return state
}

const mapDispatchToProps = (dispatch) => {
  return {
    RemoveAction: (index) => {dispatch({type: 'DELETE',index})}
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(TodoList)

複製代碼

本文參考:

相關文章
相關標籤/搜索