Redux从入门到进阶,看这一篇就够了!

Posted 星期一研究室

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redux从入门到进阶,看这一篇就够了!相关的知识,希望对你有一定的参考价值。

🌂序言

大家都知道, react单向数据流,所以它传递数据也较为简单,父子之间的关系也较为明确。但是呢,如果我们要做更多复杂数据的传递,单单使用 react 是完全不够的。因此,我们需要用到 redux 来做更为复杂的数据传递。

那在下面的这篇文章中,将从入门到进阶,讲解 redux 的工作流程。

叮!开始 redux 之旅吧~👏

☂️一、基础知识

1、Redux概念简述

对于 react 来说,它是一个非视图层的轻量级框架,如果要用它来传递数据的话,则要先父传子,然后再慢慢地一层一层往上传递。

但如果用 redux 的话,假设我们想要某个组件的数据,那这个组件的数据则会通过 redux 来存放到 store 中进行管理。之后呢,通过 store ,再来将数据一步步地往下面的组件进行传递。

值得注意的是,我们可以视 ReduxReducerFlux 的结合。

2、Redux的工作流程

Redux ,实际上就是一个数据层的框架,它把所有的数据都放在了 store 之中。我们先来看一张图:

大家可以看到中间的 store ,它里面就存放着所有的数据。继续看 store 向下的箭头,然后呢,每个组件都要向 store 里面去拿数据。

我们用一个例子来梳理整张图,具体如下:

  • ①整张图上有一个 store ,它存放着所有的数据,也就是存储数据的公共区域
  • ②每个组件,都要从 store 里面拿数据;
  • ③假设现在有一个场景,模拟我们要在图书馆里面借书。那么我们可以把 react Component 理解为借书人,之后呢,借书人要去找图书馆管理员才能借到这本书。而借书这个过程中数据的传递,就可以把它视为是 Action Creators ,可以理解为 “你想要借什么书” 这句话。
  • Action Creatures 去到 store 。这个时候我们把 store 当做是图书馆管理员,但是,图书馆管理员是没有办法记住所有图书的数据情况的。一般来说,它都需要一个记录本,你想要借什么样的书,那么她就先查一下;又或者你想要还什么书,她也要查一下,需要放回什么位置上。
  • ⑤这个时候就需要跟 reducers 去通信,我们可以把 reducers 视为是一个记录本,图书馆管理员用这个记录本来记录需要的数据。管理员 store 通过 reducer 知道了应该给借书人 Components 什么样的数据。

🎃二、使用Antd实现TodoList页面布局

1、在项目中使用Antd

打开 antdesign 的官网👉antd官网传送门,我们先来在项目中引入它。具体步骤如下:

第一步,安装 antd命令如下:

npm install antd --save

第二步,引入样式。代码如下:

import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'

2、使用Antd实现TodoList的基本布局

首先,我们在项目的 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' }} />
          <Button type="primary">提交</Button>
        </div>
        <List
          bordered
          dataSource={data}
          renderItem={item => <List.Item>{item}</List.Item>}
        />
      </div>
    )
  }
}

export default TodoList;

此时浏览器的显示效果为:

3、创建redux中的store

(1)创建store

接下来我们来创建项目中的 store 。首先在项目的 src 文件夹下创建一个新的文件夹,命名为 store 。接着,我们在 store 文件夹下面,创建一个新的文件,命名为 index.js具体代码如下:

import { createStore } from "redux";
import reducer from './reducer';

const store = createStore(reducer);

export default store;

然后呢,继续在 store 文件夹下面创建一个新的文件,命名为 reducer.js具体代码如下:

const defaultStore = {
  inputValue: '',
  list: []
};

export default (state = defaultStore, action) => {
  return state;
}

由此,我们就创建完成了项目中的 store

(2)在项目中使用store

创建完 store 之后,我们先初步在项目中使用这个 store ,以便于看看效果。先来添加 store 中的数据,首先改造在 store 中的 reducer.js 文件,具体代码如下:

const defaultStore = {
  inputValue: '123',
  list: [1, 2]
};

export default (state = defaultStore, action) => {
  return state;
}

之后改造 TodoList.js具体代码如下:

import React, { Component } 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()
  }
  render() {
    return (
      <div style={{marginTop: '10px', marginLeft: '10px'}}>
        <div>
          <Input placeholder={this.state.inputValue} style={{ width: '300px' }} />
          <Button type="primary">提交</Button>
        </div>
        <List
          bordered
          dataSource={this.state.list}
          renderItem={item => <List.Item>{item}</List.Item>}
        />
      </div>
    )
  }
}

export default TodoList;

此时浏览器的显示效果为:

🧵三、Action和Reducer的编写 - 增添功能

1、主体页面内容改造

接下来,我们使用 actionreducer ,来对这个组件的数据进行前后传递。首先,先来改造 TodoList.js 文件。具体代码如下:

import React, { Component } 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)
  }
  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) {
    // 在react中,action是一个对象的形式
    // type旨在告诉react说,你帮我去改变input的值,这个值是下面的value,也就是e.target.value
    const action = {
      type: 'change_input_value',
      value: e.target.value
    }
    store.dispatch(action)
    // console.log(e.target.value)
  }

  handleStoreChange() {
    // 当感知到store的数据发生变化时,那么就去调用store.getState方法,从store里面再重新取一次数据,
    // 然后去调用setState,替换掉当前store里面的数据
    this.setState(store.getState())
  }

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

export default TodoList;

接下来我们来分析以上代码。首先,每一个动作分别会先去绑定对应的事件,之后呢,在事件里面,去创造 action 。而对于创造的 action 来说,它旨在告诉 react ,让 react 去帮忙 action 去改变某个值,而这个值就是它绑定的 value

最后, action 要做的事情结束了,那么它的数据就需要去存储到 store 里面。于是通过 store.dispatch(action) 来进行处理,将 action 的数据传递到 store 里面。

2、改变action中的数据

对于 action 一开始的值来说,它是固定的。但有时候我们是想要去修改action中的值,这个时候就需要用到 reducer 。现在,我们来改造下 reducer.js 文件,让 input 框可以自由的输入值,同时,点击提交按钮之后,进行列表的增添操作。具体代码如下:

const defaultStore = {
  inputValue: '123',
  list: [1, 2]
};

// reducer 可以接收state,但是绝不能修改state
const reducer = (state = defaultStore, 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 = '';
    console.log(newState);
    return newState;
  }
  return state;
}
export default reducer;

3、store数据改造

下面,我们来看下 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;

除了 reducer 之外,我们还要将 window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() 给传递进去并调用这个方法。

最后,我们来看下浏览器的显示效果:

🧶四、使用Redux实现TodoList的删除功能

1、对组件进行事件绑定

上面我们实现了增添功能,那么现在,我们继续来实现删除功能,实现每点击每一项时,能够删除点击项的数据。先来在 TodoList.js 文件中绑定对应的事件,具体代码如下:

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

class TodoList extends Component {
  constructor(props) {
    // 此处省略上述已有代码
  }
  render() {
    return (
      {/* 此处省略上述已有代码 */}
        <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中进行数据通信

接着,我们在 reducer.js 文件中,对数据进行通信。具体代码如下:

const defaultStore = {
  inputValue: '123',
  list: [1, 2]
};

// reducer 可以接收state,但是绝不能修改state
const reducer = (state = defaultStore, action) => {
  // 此处省略上述已有代码
    
  if (action.type === 'delete_todo_item') {
    const newState = JSON.parse(JSON.stringify(state));
    newState.list.splice(action.index, 1);
    return newState;
  }
  return state;
}
export default reducer;

现在,我们来看下浏览器的显示效果:

👓五、逻辑归纳

1、ActionTypes的拆分

在上面的 TodoList.js 中,大家可以看到,我们会频繁地去操作 action 。同时,假设说其中的 type 如果我们稍微写错了一个字母,那排错的过程总是不好定位的。

因此,我们要来做的一件事情就是 ActionTypes 的拆分。

首先,我们在 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';

其次,改造 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() {
    this.setState(store.getState())
  }

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

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

export default TodoList;

最后,改造 reducer.js 文件。具体代码如下:

import {
  CHANGE_INPUT_VALUE,
  ADD_TODO_ITEM,
  DELETE_TODO_ITEM
} from './actionTypes';

const defaultStore = {
  inputValue: '123',
  list: [1, 2]
};

const reducer = (state = defaultStore, 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 = '';
    console.log(newState);
    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;
}
export default reducer;

通过将 change_input_valueadd_todo_itemdelete_todo_item 进行整合,将其整合到 actionTypes.js 文件下,这样,如果我们遇到字母写错的情况下,也能够更好的进行排错。

2、使用actionCreator统一创建action

在上面的 TodoList.js 中,大家可以看到,对于几个绑定的事件来说,我们总是要频繁的去创建 action ,重复性

以上是关于Redux从入门到进阶,看这一篇就够了!的主要内容,如果未能解决你的问题,请参考以下文章

Redux从入门到进阶,看这一篇就够了!

Vue3 核心技能从入门到难点攻破,看这一篇就够了!

Docker从入门到精通,看这一篇就够了

288页的python编程文档,从入门到实践,入门看这一篇就够了

C++Cmake使用教程(看这一篇就够了)

Elasticsearch入门,看这一篇就够了