06.Redux入门

2020-07-14  本文已影响0人  小二的学习日记

Redux概念简述

image.png

Redux的工作流程

image.png

使用Antd实现TodoList页面布局

这里,我们新建一个项目
1.cmd命令行运行
creat-react-app reduxdemo
2.根据之前学过的内容,整理项目到最简状态(在01这个文章里)
3.导入antUI
npm install antd --save
4.编写新的TodoList
这里
Input
Button
List
这三给组件可以从AntDesign官网里去找使用方法

//===>src/TodoList.js
import React, { Component } from 'react'
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';

const data = [
    'Racing car sprays burning fuel into crowd.',
    'Japanese princess to wed commoner.',
    'Australian walks 100km after outback crash.',
    'Man charged over missing wedding girl.',
    'Los Angeles battles huge wildfires.',
];

class TodoList extends Component {
    render() {
        return (
            <div style={{ marginTop: '10px', marginLeft: '10px' }}>
                <div>
                    <Input placeholder="todo info" style={{ width: '300px', marginRight: '10px' }} />
                    <Button type="primary">提交</Button>
                </div>
                <List
                    style={{ marginTop: '10px', width: '300px' }}
                    bordered
                    dataSource={data}
                    renderItem={item => <List.Item>{item}</List.Item>}
                />
            </div>
        )
    }
}

export default TodoList

5.导入这个组件到项目

//===>src/App.js
import React from 'react';
import TodoList from './TodoList'

function App() {
  return (
    <div className="App">
      <TodoList />
    </div>
  );
}

export default App;
效果

创建redux中的store

1.我们先创建reducer.js文件,defaultState定义要存储的数据对象,并传给state。
2.接着,我们把存数据的reducer,传给store。后面我们就可以从store里面取数据了
3.在TodoList文件中,通过store.getState()拿到store,就可以使用store的内容了。
1.安装redux
cnpm install --save redux
2.创建reducer.js

//===>src/store/reducer.js
const defaultState = {
    inputValue: '123',
    list: [1, 2]
}
export default (state = defaultState, action) => {
    return state;
}

3.创建store

//===>src/store/index.js
import { createStore } from 'redux'
import reducer from './reducer'

const store = createStore(reducer);

export default store;

4.在TodoList组件中,使用redux

import React, { Component } from 'react'
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';
import store from './store/index.js'

class TodoList extends Component {

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

    render() {
        return (
            <div style={{ marginTop: '10px', marginLeft: '10px' }}>
                <div>
                    <Input value={this.state.inputValue} placeholder="todo info" style={{ width: '300px', marginRight: '10px' }} />
                    <Button type="primary">提交</Button>
                </div>
                <List
                    style={{ marginTop: '10px', width: '300px' }}
                    bordered
                    dataSource={this.state.list}
                    renderItem={item => <List.Item>{item}</List.Item>}
                />
            </div>
        )
    }
}

export default TodoList
效果

Action和Reducer的编写

1.为了更好的研究redux,我们需要在浏览器安装redux-devtools-extension这个插件(需要科学上网)。然后按照文档提示,我们在store文件中添加这样一句话:

//===>src/store/index.js
import { createStore } from 'redux'
import reducer from './reducer'

const store = createStore(
    reducer,
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());

export default store;

2.回忆一下这个图。我们现在要编写的功能是,在输入框输入内容(蓝色),然后点击确定,组件触发store的dispatch发送action(黄色)。store调用dispatch方法,将action发送给了store(橙色),store不能处理数据,所以他去找reducer仓库(紫色)修改数据。reducer通过传过来的action生成了新的state,return给store。最后store真正的拿到了新的数据,并会触发组件内容的改变。


回忆

下面我们实现这个过程。
3.修改TodoList

//===>src/TodoList.js
import React, { Component } from 'react'
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';
import store from './store/index.js'


class TodoList extends Component {

    constructor(props) {
        super(props);
        this.state = store.getState()
        this.handleInputChange = this.handleInputChange.bind(this)
        this.handleStoreChange = this.handleStoreChange.bind(this)
        this.handleBtnClick = this.handleBtnClick.bind(this)
        store.subscribe(this.handleStoreChange)
    }

    render() {
        return (
            <div style={{ marginTop: '10px', marginLeft: '10px' }}>
                <div>
                    <Input
                        value={this.state.inputValue}
                        placeholder="todo info"
                        style={{ width: '300px', marginRight: '10px' }}
                        onChange={this.handleInputChange} />
                    <Button type="primary" onClick={this.handleBtnClick}>提交</Button>
                </div>
                <List
                    style={{ marginTop: '10px', width: '300px' }}
                    bordered
                    dataSource={this.state.list}
                    renderItem={item => <List.Item>{item}</List.Item>}
                />
            </div>
        )
    }

    handleInputChange(e) {
        const action = {
            type: 'change_input_value',
            value: e.target.value
        }
        store.dispatch(action)
    }

    handleStoreChange(e) {
        this.setState(store.getState())
    }

    handleBtnClick(e) {
        const action = {
            type: 'add_todo_item'
        }
        store.dispatch(action)
    }
}

export default TodoList

4.修改reducer

//===>src/store/reducer.js
const defaultState = {
    inputValue: '123',
    list: [1, 2]
}

// reducer可以接收state,但是绝不能修改state
export default (state = defaultState, action) => {
    if (action.type === 'change_input_value') {
        const newState = JSON.parse(JSON.stringify(state))
        newState.inputValue = action.value
        return newState
    }
    if (action.type === 'add_todo_item') {
        const newState = JSON.parse(JSON.stringify(state))
        newState.list.push(newState.inputValue)
        newState.inputValue = ''
        return newState
    }
    return state;
}
我们配合插件看下效果

使用Redux完成TodoList删除功能

1.给List.Item添加点击事件

//===>src/TodoList.js
...
class TodoList extends Component {

    constructor(props) {
       ...
    }

    render() {
        return (
            <div style={{ marginTop: '10px', marginLeft: '10px' }}>
                ...
                <List
                    style={{ marginTop: '10px', width: '300px' }}
                    bordered
                    dataSource={this.state.list}
                    renderItem={(item, index) => <List.Item onClick={this.handleItemDelete.bind(this, index)}>{item}</List.Item>}
                />
            </div>
        )
    }
...
    handleItemDelete(index) {
        const action = {
            type: 'delete_todo_item',
            index
        }
        store.dispatch(action)
    }
}

export default TodoList

2.添加reducer中的删除逻辑

//===>src/store/reducer.js
const defaultState = {
    inputValue: '',
    list: []
}

// reducer可以接收state,但是绝不能修改state
export default (state = defaultState, action) => {
...
    if (action.type === 'delete_todo_item') {
        const newState = JSON.parse(JSON.stringify(state))
        newState.list.splice(action.index, 1)
        return newState
    }
    return state;
}
image.png

ActionTypes的拆分

在这个例子中 我们要把change_input_value、add_todo_item、delete_todo_item这些action弄成全局的。
1.编写actionTypes文件

//===>src/store/actionTypes.js
export const CHANGE_INPUT_VALUE = 'change_input_value';
export const ADD_TODO_ITEM = 'add_todo_item';
export const DELETE_TODO_ITEM = 'delete_todo_item';

2.修改TodoList.js

//===>src/TodoList.js
...
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './store/actionTypes'

class TodoList extends Component {
...
    handleInputChange(e) {
        const action = {
            type: CHANGE_INPUT_VALUE,
            value: e.target.value
        }
        store.dispatch(action)
    }

    handleStoreChange(e) {
        this.setState(store.getState())
    }

    handleBtnClick(e) {
        const action = {
            type: ADD_TODO_ITEM
        }
        store.dispatch(action)
    }

    handleItemDelete(index) {
        const action = {
            type: DELETE_TODO_ITEM,
            index
        }
        store.dispatch(action)
    }
}

export default TodoList

3.修改reducer.js

//===>src/store/reducer.js
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './actionTypes'

const defaultState = {
    inputValue: '',
    list: []
}

// reducer可以接收state,但是绝不能修改state
export default (state = defaultState, action) => {
    if (action.type === CHANGE_INPUT_VALUE) {
        const newState = JSON.parse(JSON.stringify(state))
        newState.inputValue = action.value
        return newState
    }
    if (action.type === ADD_TODO_ITEM) {
        const newState = JSON.parse(JSON.stringify(state))
        newState.list.push(newState.inputValue)
        newState.inputValue = ''
        return newState
    }
    if (action.type === DELETE_TODO_ITEM) {
        const newState = JSON.parse(JSON.stringify(state))
        newState.list.splice(action.index, 1)
        return newState
    }
    return state;
}

使用actionCreator统一创建action

1.编写actionCreator

//===>src/store/actionCreators.js
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './actionTypes'

export const getInputChangeAction = (value) => ({
    type: CHANGE_INPUT_VALUE,
    value
})

export const getAddItemAction = () => ({
    type: ADD_TODO_ITEM,
})

export const getDeleteItemAction = (index) => ({
    type: DELETE_TODO_ITEM,
    index
})

2.把actionCreator引入TodoList

//===>src/TodoList.js
import React, { Component } from 'react'
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';
import store from './store/index.js'
import { getInputChangeAction, getAddItemAction, getDeleteItemAction } from './store/actionCreators'

class TodoList extends Component {

    constructor(props) {
        ...
    }

    render() {
        return (
           ...
        )
    }

    handleInputChange(e) {
        const action = getInputChangeAction(e.target.value)
        store.dispatch(action)
    }

    handleStoreChange(e) {
        this.setState(store.getState())
    }

    handleBtnClick(e) {
        const action = getAddItemAction()
        store.dispatch(action)
    }

    handleItemDelete(index) {
        const action = getDeleteItemAction(index)
        store.dispatch(action)
    }
}

export default TodoList

Redux知识点复习补充

纯函数

纯函数指的是,给定固定的输入,就一定会有固定的输出,而且不会有副作用。
store是唯一的
只有store能够改变自己的内容
Reducer必须是顺函数

Redux的核心API

createStore
store.dispatch
store.getState
store.subscribe

上一篇下一篇

猜你喜欢

热点阅读