Redux入门

一. Redux 概念简述

        React 本身是一个视图层的框架,我们想要开发比较大型的项目,还需要 Redux 这个数据层的框架来帮助我们。下图就是是否使用了Redux 进行传值的比较。

        

        Redux: 把组件所有的数据都放到公用的存储空间 Store 里,当绿色组件想要改变数据传递给其它组件的时候,只需要改变 Store 里面对应的数据即可,不需要一层一层地去传值。灰色组件会自动去感知到 Store 里面数据的变化,然后重新去获取数据。

        

        Flux:最原始的官方提供的辅助 React 开发的数据层框架,但是在使用时会存在一些缺点,它的公共存储区域 Stroe 里面可以有很多个 Store ,这就会产生互相依赖的问题。Redux 就是 Flux 的升级,添加了 Reducer。

 


二.   Redux 的工作流程   

Store:存储数据的公共区域 ;(理解成图书馆管理员)

React Components :每一个 React 组件 ;(视为借书的人)

Action Creators :(当我在图书馆借书的时候,我要和管理员说借什么书,那么这个语句的表达和传递就可以理解为 Action Creators );

Reducers :(记录图书的记录本);

实际理解:

        查找过程:首先我有一个组件,这个组件要去获取 Store 中的一些数据,然后就要告诉 Store 我要获取数据,这句话就是 Action Creators ,Store 接收到了这句话以后,但是它不知道你应该给你什么样的数据,它就要去 Reducers 中查,Reducers 会告诉它应该给怎么样的数据,Store 知道了以后把对应的数据给组件即可。

        修改过程:组件先要告诉 Store ,我要改变什么数据,但是它不知道怎么去改,他就要去问 Reducers ,Reducers 就会告诉 Store 如何去改变。

 


三. 使用 Antd 实现 TodoList 页面布局

  • 首先安装 Antd :
npm install antd --save
  • 然后根据文档引入使用: 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;

 


四. 创建 redux 中的 store

  • 首先安装 Redux :
npm install --save redux
  • 项目目录结构:

  • store 下的 index.js :
/*创建一个store*/
import { createStore } from 'redux';  //引入createStore
import reducer from './reducer';

const store = createStore(reducer);  //创建一个store

export default store;
  • reducer.js :
/*建立一个reducer*/
const defaultState = {
  inputValue: '123',
  list: [1, 2]
};

export default (state = defaultState, action) => {
  //state存放的就是所有数据的信息,
  return state;
}
  • TodoList.js :
import React, { Component } from 'react';
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';
import store from './store/index';  //引入store

class TodoList extends Component {
  constructor(props) {
    super(props);
    this.state = store.getState();
    console.log(this.state);   //{inputValue: "123", list: Array(2)}
  }
  render() {
    return (
      <div style={{marginTop: '10px', marginLeft: '10px'}}>
        <div>
          <Input
            placeholder="todo info"
            style={{width: '300px', marginRight: '10px'}}
            value = {this.state.inputValue}
          />
          <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 的编写

  • store 下面的 index.js :
/*创建一个store*/
import { createStore } from 'redux';  //引入createStore
import reducer from './reducer';

const store = createStore(
    reducer,
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() //配置redux devtools
  );  //创建一个store

export default store;
  • reducer.js :
/*建立一个reducer*/
const defaultState = {
  inputValue: '',
  list: [1, 2]
};

//reducer可以接收state,但是绝不可以修改state
export default (state = defaultState, action) => {
  //state上一次存储的数据,action就是组件通过store传过来
  if( action.type === 'change_input_value' ) {
    const newState = JSON.parse(JSON.stringify(state));  //深拷贝
    newState.inputValue = action.value;
    return newState;  //返回给store,store就会拿新的数据替换掉老的数据
  }
  if( action.type === 'add_todo_item' ) {
    const newState = JSON.parse(JSON.stringify(state));
    newState.list.push(newState.inputValue);
    newState.inputValue = '';
    return newState;
  }
  return state;
}
  • TodoList.js :
import React, { Component } from 'react';
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';
import store from './store/index';  //引入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发生改变就执行handleStoreChange这个函数
  }
  render() {
    return (
      <div style={{marginTop: '10px', marginLeft: '10px'}}>
        <div>
          <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={{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() {
    this.setState(store.getState());  //当我感知到store的数据发生变化的时候,就去调用store.getState方法,从store里重新取一次数据,然后通过this.setState替换掉当前组件的数据
  }
  handleBtnClick() {
    const action = {
      type: 'add_todo_item',
    };
    store.dispatch(action);
  }
}

export default TodoList;

六. 使用 Redux 完成 TodoList 的删除功能

  • reducer.js :
/*建立一个reducer*/
const defaultState = {
  inputValue: '',
  list: [1, 2]
};

//reducer可以接收state,但是绝不可以修改state
export default (state = defaultState, action) => {
  //state上一次存储的数据,action就是组件通过store传过来
  if( action.type === 'change_input_value' ) {
    const newState = JSON.parse(JSON.stringify(state));  //深拷贝
    newState.inputValue = action.value;
    return newState;  //返回给store,store就会拿新的数据替换掉老的数据
  }
  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;
}
  • TodoList.js:
import React, { Component } from 'react';
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';
import store from './store/index';  //引入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发生改变就执行handleStoreChange这个函数
  }
  render() {
    return (
      <div style={{marginTop: '10px', marginLeft: '10px'}}>
        <div>
          <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={{marginTop: '10px', width: '300px'}}
          bordered
          dataSource={this.state.list}
          renderItem={
            (item, index) => <List.Item onClick={this.handleDleleteItem.bind(this, index)}> {item}</List.Item>
          }
        />
      </div>
    )
  }
  handleInputChange(e) {
    const action = {
      type: 'change_input_value',
      value: e.target.value
    }
    store.dispatch(action);
  }
  handleStoreChange() {
    this.setState(store.getState());  //当我感知到store的数据发生变化的时候,就去调用store.getState方法,从store里重新取一次数据,然后通过this.setState替换掉当前组件的数据
  }
  handleBtnClick() {
    const action = {
      type: 'add_todo_item',
    };
    store.dispatch(action);
  }
  handleDleleteItem(index) {
    const action = {
      type: 'delete_todo_item',
      index
    }
    store.dispatch(action);
  }
}

export default TodoList;

七. ActionTypes 的拆分

action的type太多了,有时候因为拼写错误导致很长事件定位不到bug。我们可以用一个 js 文件来保存。

  • 在 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';
  • reducer.js :
/*建立一个reducer*/
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './actionTypes';
const defaultState = {
  inputValue: '',
  list: [1, 2]
};

//reducer可以接收state,但是绝不可以修改state
export default (state = defaultState, action) => {
  //state上一次存储的数据,action就是组件通过store传过来
  if( action.type === CHANGE_INPUT_VALUE ) {
    const newState = JSON.parse(JSON.stringify(state));  //深拷贝
    newState.inputValue = action.value;
    return newState;  //返回给store,store就会拿新的数据替换掉老的数据
  }
  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;
}

同理,TodoList.js 也引入后将字符串更换成变量。


八. 使用 actionCreator 统一创建 action

一般来说都会用一个 actionCreator 来统一地去创建管理 action , 而不是在组件中管理。

  • store 下面创建一个 actionCreators.js :
import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './actionTypes';

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

export const getHandleBtnClick = () => ({
  type: ADD_TODO_ITEM
})

export const getHandleDleleteItem = (index) => ({
  type: DELETE_TODO_ITEM,
  index
})
  • TodoList.js :
import React, { Component } from 'react';
import 'antd/dist/antd.css';
import { Input, Button, List } from 'antd';
import store from './store/index';  //引入store
import { getHandleInputChangeAction, getHandleBtnClick, getHandleDleleteItem } from './store/actionCreators'

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发生改变就执行handleStoreChange这个函数
  }
  render() {
    return (
      <div style={{marginTop: '10px', marginLeft: '10px'}}>
        <div>
          <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={{marginTop: '10px', width: '300px'}}
          bordered
          dataSource={this.state.list}
          renderItem={
            (item, index) => <List.Item onClick={this.handleDleleteItem.bind(this, index)}> {item}</List.Item>
          }
        />
      </div>
    )
  }
  handleInputChange(e) {
    const action = getHandleInputChangeAction(e.target.value);
    store.dispatch(action);
  }
  handleStoreChange() {
    this.setState(store.getState());  //当我感知到store的数据发生变化的时候,就去调用store.getState方法,从store里重新取一次数据,然后通过this.setState替换掉当前组件的数据
  }
  handleBtnClick() {
    const action = getHandleBtnClick();
    store.dispatch(action);
  }
  handleDleleteItem(index) {
    const action = getHandleDleleteItem(index);
    store.dispatch(action);
  }
}

export default TodoList;

这么写的话我们代码的可维护性更高。

 


九. Redux 知识点复习补充

1、Redux 设计和使用的三个原则:

  • store 是唯一的 ;
  • 只有 store 能够改变自己的内容 ;( reducer 只是把内容返回给 store )
  •  reducer 必须是纯函数 ;(纯函数:给定固定的输入,就一定会有固定的输出,而且不会有任何副作用)

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值