React学习笔记(Chapter5)

五、Redux入门

5-1 Redux概念简述

Redux 是 JavaScript 状态容器,提供可预测化的状态管理。 (如果你需要一个 WordPress 框架,请查看 Redux Framework。)
可以让你构建一致化的应用,运行于不同的环境(客户端、服务器、原生应用),并且易于测试。不仅于此,它还提供 超爽的开发体验,比如有一个时间旅行调试器可以编辑后实时预览。
Redux 除了和 React 一起用外,还支持其它界面库。 它体小精悍(只有2kB,包括依赖)。

5-2 Redux的工作流程

在这里插入图片描述
把它们理解成:借书的用户(React Components)、要借什么书(Action)、图书管理员(Store)、记录本(Reducers);以此来更清楚地理解工作流程。

5-3 使用antd实现TodoList页面布局

步骤:
  1. 安装antd

yarn add antd

  1. 关键代码
//导入包
import 'antd/dist/antd.css';
import { Input,Button,List } from 'antd';
//第一个div代码段
<label htmlFor="insertArea">输入内容</label>
<Input
   placeholder="todo info"
    style={{width: '300px',marginRight: '10px'}}
    value={this.state.inputValue}
/>
<Button
    type="primary"
    onClick={this.handleBtnClick}
>提交</Button>
  1. 最终效果:
    输入框、按钮、表均为antdesign样式。
    在这里插入图片描述

5-4 创建Redux中的store

步骤:
  1. 安装 store

(in Terminal)yarn add redux

  1. 在src根目录下创建store文件夹,在其中新建index.js,reducer.js,内容如下
//index.js
import { createStore } from "redux";
import reducer from './reducer';

const store = createStore(reducer);

export default store;
//reducer.js
const defaultState = {
    inputValue: '123',  //测试数据
    list: [1,2,3]		//测试数据
}

export default (state = defaultState, action) => {  //两个“必须”的值
    return state;
}
  1. 在TodoList.js内引用store,并且连接数据
//TodoList.js
...
import store from './store';        	    //在TodoList.js内引用store
class TodoList extends Component{
	constructor(props) {
	        ...
	        this.state = store.getState();  //导入reducer中的数据到state
	        console.log(this.state); 		//仅作监视用
	    }
	return (
	            <Fragment>
	                ...
	                <List
	                    ...
	                    dataSource={this.state.list} //导入state中的数据到表
	                    ...
	                />
	            </Fragment>
	        )
	...
}
  1. 最终效果:
    ①reducer中的list里的数据可以被打印在表中。
    ②reducer中inputvalue的值被打印在输入框里。
    在这里插入图片描述

5-5 Action和Reducer的编写

  1. 安装插件Redux Devtools,插件界面

在这里插入图片描述

实现:
  1. 代码
//TodoList.js
import React, { Component,Fragment } from 'react';
import 'antd/dist/antd.css';
import { Input,Button,List } from 'antd';
import store from './store';

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); //监听store,当其发生变化时,处理函数
    }
    render() {
        return (
            <Fragment>
                <div style={{marginTop: '10px',marginLeft: '10px'}}>
                    <label htmlFor="insertArea">输入内容</label>
                    <Input
                        placeholder="todo info"
                        style={{width: '300px',marginRight: '10px'}}
                        value={this.state.inputValue}
                        onChange={this.handleInputChange}
                    />
                    <Button
                        type="primary"
                        onClick={this.handleBtnClick}
                    >提交</Button>
                </div>
                <List
                    style={{width: '440px', marginTop: '10px', marginLeft: '10px'}}
                    bordered
                    dataSource={this.state.list}
                    renderItem={item => <List.Item>{item}</List.Item>}
                />
            </Fragment>
        )
    }



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

    handleBtnClick(){
        const action = {
            type: 'change_todo_item'
        }
        store.dispatch(action);
    }


    handleStoreChange() {
        this.setState(store.getState());//同步最新数据
    }

}

export default TodoList;
//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;
//store/reducer.js
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)); //深拷贝state到newstate
        newState.inputValue = action.value;
        return newState; //返回给Store
    }
    if (action.type === 'change_todo_item') {
        const newState = JSON.parse(JSON.stringify(state));
        newState.list.push(newState.inputValue);
        newState.inputValue = '';
        console.log(newState);
        return newState;
    }
    return state;
}
  1. 测试结果:

在这里插入图片描述
目前为止,只实现了TodoList的添加功能,删除功能及代码优化会在后面几节中实现。

----------本章未完待续----------

5.6 使用Redux完成TodoList

//TodoList.js
//在原来<List/>中修改
renderItem={(item, index) => <List.Item onClick={this.handleItemDelete.bind(this, index)}>{item}</List.Item>}//给每个List.item绑定事件
//写这个事件的方法
handleItemDelete(index) {
        const action = {
            type: 'delete_todo_item',
            index
        }
        store.dispatch(action);
    }
//reducer.js
//新增一个删除功能
...
if (action.type === 'delete_todo_item') {  
        const newState = JSON.parse(JSON.stringify(state));
        newState.list.splice(action.index, 1);
        return newState;
    }

5.7 ActionTypes的拆分

常量和变量的错误能直接被提示出来,而字符串的错误是不会被提示出来的。如果庞大的代码因为字符串的错误而不能实现某功能,这时候错误是很难被发现的。为了便于找到代码的错误,这里建议把字符串action.type转换成常量的形式存在文件中。

步骤:
  1. 在store文件夹下新建actionTypes.js,在其中进行“字符串action.type转换成常量”的操作。
//把字符串转换成常量的形式
export const CHANGE_INPUT_VALUE = 'change_input_value';
export const CHANGE_TODO_ITEM ='change_todo_item';
export const DELETE_TODO_ITEM = 'delete_todo_item';
  1. 在TodoList.js和reducer.js中引用这些常量,并把原来字符串的地方替换成对应的常量。
  2. 保存,运行网页,能正常运行。

5.8 使用actionCreator统一创建action

为了进一步提高代码的可维护性,和方便自动化测试,我们使用actionCreator统一创建action

步骤:
  1. 在store文件夹下新建actionCreators.js。
  2. 编写actionCreators.js。
//引用actionTypes里的常量,封装成函数的形式,导出给TodoList.js
import { CHANGE_INPUT_VALUE, CHANGE_TODO_ITEM, DELETE_TODO_ITEM } from "./actionTypes";

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

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

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


  1. 在TodoList.js中引用这些函数,并把原来const action等号之后调用这些函数。
    下面是例子:
handleInputChange(e) {
        const action = getInputChangeAction(e.target.value);
        store.dispatch(action);
    }

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


    handleStoreChange() {
        this.setState(store.getState());//同步最新数据
    }

    handleItemDelete(index) {
        const action = getDeleteItemAction(index);
        store.dispatch(action);
    }
  1. 保存,运行网页,能正常运行。

5.9 Redux总结

  1. store必须是唯一的,它是公共存储空间。
  2. 只有store能改变自己的内容。
  3. reducer必须是纯函数。纯函数是指,给定固定的输入(在任何时间下),就一定有固定的输出,而且不会有任何副作用。不是纯函数的例子:输出和日期相关、ajax请求等等。副作用是指,直接对参数state的修改,而不是对newstate的修改。
  4. Redux的核心api:
名称作用
creatStore帮助创建store
store帮助派发action传递给store
store.getState帮助获取store中的内容
store.subscribe订阅store的改变,只要store发生改变,执行回调函数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值