源码地址

https://github.com/Hard-workingrookie/react-redux-todolist

react-redux编写TodoList

React-Redux介绍和安装

React-Redux 是react生态中的常用组件,可以理解为全局数据状态管理工具(状态管理机),用来做组件通信等。

react项目初始化

1
2
3
4
create-react-app demo02
cd demo02
npm start

或者

1
yarn start

安装完成后,删除一些没有必要的样式和代码,在/src目录下,只留一个index.js文件,其余的全部删除,这时候项目已经不能启动起来了,这很正常。

1
2
3
4
import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(<App />, document.getElementById('root'));

我们把APP删除了,所以会报错,不过没有关系,我们之后再来处理这个。

react-redux

项目初始化好后,直接使用npm在命令行安装React-redux,这个网络的不同安装时间也有所不同。

1
npm install --save react-redux

或者

1
yarn add react-redux --save

修改代码,跑起来

建立一个TodoList.js的组件

1
2
3
4
5
6
7
8
import React, { Component } from 'react';
class TodoList extends Component {
render() {
return ( <div>LitterWang</div> );
}
}
export default TodoList;

将TodoList.js引入到index.js文件下:

1
2
3
4
import React from 'react';
import ReactDOM from 'react-dom';
import TodoList from './TodoList'
ReactDOM.render(<TodoList />, document.getElementById('root'));

这时候再在浏览器中预览,就会只输出一个LitterWang的字样。虽然很丑,但是项目已经跑起来了。接下来我们编写一下render函数中的JSX页面。

1
2
3
4
5
6
7
8
9
10
render() { 
return (
<div>
<div><input /><button>提交</button></div>
<ul>
<li>LitterWang</li>
</ul>
</div>
);
}

这时候界面应该发生了一点变化,这样基本的项目我们就算初始化完成了,接下来我们按原来的Redux方式作一个store出来。

Redux 的安装和使用(复习)

先在终端中安装Redux包,因为是一个新项目,所以需要重新安装。

1
2
3
npm install --save redux

yarn add redux --save

创建一个store文件夹,在/store下创建一个index.js文件,并写入下面代码:

1
2
3
4
5
6
7
import {createStore} from 'redux'
import reducer from './reducer'

const store = createStore(reducer)

export default store

创建reducer.js文件,代码如下:

1
2
3
4
5
6
7
8
const defalutState = {
inputValue : 'LitterWang',
list :[]
}

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

然后再TodoList.js中的构造函数constructor中使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

import React, { Component } from 'react';
//-----关键代码--------start
import store from './store'
//-----关键代码--------end
class TodoList extends Component {
//-----关键代码--------start
constructor(props){
super(props)
this.state = store.getState()
}
//-----关键代码--------end
render() {
return (
<div>
<div>
//-----关键代码--------start
<input value={this.state.inputValue} />
//-----关键代码--------end
<button>提交</button>
</div>
<ul>
<li>JSPang</li>
</ul>
</div>
);
}
}

export default TodoList;

写完这段,到浏览器中保存看一下,应该就得到store中的值了,到目前为止,我们只是安装了React-Redux,但是还并没有进行使用,这节课只要是把基本的环境搭建好和复习一下以前的知识。下节课我们再逐步学习React-Redux的知识,小伙伴们先不要着急,先把开发环境搭建好吧。

Provider和connect

上节课已经完成了React-redux开发TodoList组件的基本环境。现在就可以开心的学习React-redux了,这节课主要学习一下Provider和connect这两个知识点。

Provider 提供器

是一个提供器,只要使用了这个组件,组件里边的其它所有组件都可以使用store了,这也是React-redux的核心组件了。有了就可以把/src/index.js改写成下面的代码样式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React from 'react';
import ReactDOM from 'react-dom';
import TodoList from './TodoList'
//---------关键代码--------start
import { Provider } from 'react-redux'
import store from './store'
//声明一个App组件,然后这个组件用Provider进行包裹。
const App = (
<Provider store={store}>
<TodoList />
</Provider>
)
//---------关键代码--------end
ReactDOM.render(App, document.getElementById('root'));

完这个,我们再去浏览器中进行查看,发现代码也是可以完美运行的。需要注意的是,现在还是用传统方法获取的store中的数据。有了Provider再获取数据就没有那么麻烦了。

connect 连接器

现在如何简单的获取store中数据那?先打开TodoList.js文件,引入connect,它是一个连接器(其实它就是一个方法),有了这个连接器就可以很容易的获得数据了。

1
import {connect} from 'react-redux' 

这时候暴露出去的就变成了connect了,代码如下。

1
export default connect(xxx,null)(TodoList);

这里的xxx代表一个映射关系,目前还没有制作这个映射关系。

映射关系的制作

映射关系就是把原来的state映射成组件中的props属性,比如我们想映射inputValue就可以写成如下代码。

1
2
3
4
5
const stateToProps = (state)=>{
return {
inputValue : state.inputValue
}
}

这时候再把xxx改为stateToProps

1
export default connect(stateToProps,null)(TodoList)

然后把input里的state标签,改为props,代码如下:

1
<input value={this.props.inputValue} />

为了方便你学习,我这里给出所有的TodoList.js的所有代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import React, { Component } from 'react';
import store from './store'
import {connect} from 'react-redux'

class TodoList extends Component {
constructor(props){
super(props)
this.state = store.getState()
}
render() {
return (
<div>
<div>
<input value={this.props.inputValue} />
<button>提交</button>
</div>
<ul>
<li>JSPang</li>
</ul>
</div>
);
}
}

const stateToProps = (state)=>{
return {
inputValue : state.inputValue
}
}

export default connect(stateToProps,null)(TodoList);

写完之后再到浏览器中查看一下,发现我们映射的关系也是可以用的。这节课就是React-Redux插件的使用重点,你需要多写几遍,把这个流程记在心里。先到这里,下节课我们继续实现TodoList组件

React-redux的数据修改

上节课已经可以用React-redux顺利的拿到Store中数据了。这节课学习如何改变Store中的数据。也就是当我们修改 input 中的值时,去改变store数据,UI界面也随之进行改变。

编写 onChange 响应事件

打开TodoList.js文件,然后在 button 上注册onChange事件,这里我就偷懒直接绑定this了。

1
<input value={this.props.inputValue} onChange={this.inputChange.bind(this)} />

有了事件需要编写对应的方法,这里先写一个最简单的inputChange方法。

1
2
3
inputChange(e){
console.log(e.target.value)
}

然后到浏览器中的控制台就不再有报错,而且输入时可以打印出值,这书名我们的绑定成功了。这步完成我们要改为react-redux的了。

编写DispatchToProps

要使用react-redux,我们可以编写另一个映射DispatchToProps,先看下面这段代码,你会发现有两个参数,第二个参数我们用的是null。

1
2
3
4
5
6
7
8
9
10
export default connect(stateToProps,null)(TodoList);
DispatchToProps就是要传递的第二个参数,通过这个参数才能改变store中的值。

const dispatchToProps = (dispatch) =>{
return {
inputChange(e){
console.log(e.target.value)
}
}
}

有了这个参数之后可以把响应事件改成下面的代码.

1
<input value={this.props.inputValue} onChange={this.props.inputChange} />

然后把connect第二个参数传递过去。

1
export default connect(stateToProps,dispatchToProps)(TodoList);

这时候原来的inputChange方法就没用了,可以删除掉。 目前整体的代码就改为下面的样子了,我们在浏览器中预览也是可以看到效果的。此步骤成功说明映射关系支持成功。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import React, { Component } from 'react';
import store from './store'
import {connect} from 'react-redux'

class TodoList extends Component {
constructor(props){
super(props)
this.state = store.getState()
}
render() {
return (
<div>
<div>
<input value={this.props.inputValue} onChange={this.props.inputChange} />
<button>提交</button>
</div>
<ul>
<li>JSPang</li>
</ul>
</div>
);
}
}
const stateToProps = (state)=>{
return {
inputValue : state.inputValue
}
}

const dispatchToProps = (dispatch) =>{
return {
inputChange(e){
console.log(e.target.value)
}
}
}

export default connect(stateToProps,dispatchToProps)(TodoList);

派发action到store中

映射关系已经做好了,接下来只要进行action的派发和reducer对业务逻辑的编写就可以了。派发action和以前的流程一样,我就直接给出代码了。

1
2
3
4
5
6
7
8
9
10
11
const dispatchToProps = (dispatch) =>{
return {
inputChange(e){
let action = {
type:'change_input',
value:e.target.value
}
dispatch(action)
}
}
}

派发后就需求在reducer里边,编写对应的业务逻辑了。

1
2
3
4
5
6
7
8
9
10
11
12
const defalutState = {
inputValue : 'jspang',
list :[]
}
export default (state = defalutState,action) =>{
if(action.type === 'change_input'){
let newState = JSON.parse(JSON.stringify(state))
newState.inputValue = action.value
return newState
}
return state
}

这样就算整个修改过程完成了,到浏览器中查看一下,应该就实现了改变input框的效果。这个流程你刚开始学会觉的很绕,但是你作的多了,你就会发现它很简单,就是一个模式,而且会降低程序出错的机率。建议这个流程你至少要写5遍以上,据我所知,几乎所有公司用react都会用到react-redux,所以这个流程重要性不次于Redux的流程,一定要熟练掌握。

进阶 React-redux增加List数据

点击提交按钮时,可以在列表中进行增加。给 button 按钮增加点击事件 ,直接在/src/TodoList.js里的Button增加一个onClick事件,代码如下:

1
<button onClick={this.props.clickButton}>提交</button>

注意这里依然使用的props,也就是说还需要把方法写在dispatchToProps里。我们这里先写一个测试,看看是否绑定上了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const dispatchToProps = (dispatch) =>{
return {
inputChange(e){
let action = {
type:'change_input',
value:e.target.value
}
dispatch(action)
},
clickButton(){
console.log('111111111')
}
}
}

写完clickButton方法后,在浏览器中预览,打开浏览器的控制台看一下结果,应该在点击时,可以看到显示111111111。 这步完成,就是用dispatch派发action了。

1
2
3
4
clickButton(){
let action = { type:'add_item' }
dispatch(action)
}

编写Reducer的业务逻辑,派发完成后,到Reducer编写业务逻辑,这一步和一起的操作基本一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const defalutState = {
inputValue : 'jspang',
list :[]
}

export default (state = defalutState,action) =>{
if(action.type === 'change_input'){
let newState = JSON.parse(JSON.stringify(state))
newState.inputValue = action.value
return newState
}
//----关键代码------start---------
if(action.type === 'add_item'){
let newState = JSON.parse(JSON.stringify(state))
newState.list.push(newState.inputValue)
newState.inputValue = ''
return newState
}
//----关键代码------end---------
return state
}

页面UI部分的制作

这步完成后,我们到TodoList.js中进行JSX部分的编写,编写前需要先把stateToProps的映射关系做好。

1
2
3
4
5
6
7
const stateToProps = (state)=>{
return {
inputValue : state.inputValue,
list:state.list
}
}

有了映射关系,就可以再界面中用属性的方式,进行显示,代码如下:

1
2
3
4
5
6
7
8
<ul>
{
this.props.list.map((item,index)=>{
return (<li key={index}>{item}</li>)
})
}
</ul>

这样就实现了增加TodoList的列表项,这里给出TodoList.js的代码.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import React, { Component } from 'react';
import store from './store'
import {connect} from 'react-redux'

class TodoList extends Component {
constructor(props){
super(props)
this.state = store.getState()
}
render() {
return (
<div>
<div>
<input value={this.props.inputValue} onChange={this.props.inputChange} />
<button onClick={this.props.clickButton}>提交</button>
</div>
<ul>
{
this.props.list.map((item,index)=>{
return (<li key={index}>{item}</li>)
})
}
</ul>
</div>
);
}
}
const stateToProps = (state)=>{
return {
inputValue : state.inputValue,
list:state.list
}
}

const dispatchToProps = (dispatch) =>{
return {
inputChange(e){
let action = {
type:'change_input',
value:e.target.value
}
dispatch(action)
},
clickButton(){
let action = {
type:'add_item'
}
dispatch(action)
}
}
}
export default connect(stateToProps,dispatchToProps)(TodoList);

还有一个删除功能我就不浪费大家时间继续制作了,如果你自己有兴趣可以试着作一下。

加餐-React-redux程序优化(完结)

这节课把现在写的代码优化一下,作程序的都应该有一些代码洁癖,才能写出让人称赞的程序。写完业务逻辑后作代码优化,也是程序员的本质工作之一。 现在代码中有好几处this.props都是重复的,这时候就可以用javascript的解构赋值方法,来精简代码。修改TodoList.js中的Render函数,把原来带代码修改为下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
render() { 
let {inputValue ,inputChange,clickButton,list} = this.props;
return (
<div>
<div>
<input value={inputValue} onChange={inputChange} />
<button onClick={clickButton}>提交</button>
</div>
<ul>
{
list.map((item,index)=>{
return (<li key={index}>{item}</li>)
})
}
</ul>
</div>
);
}

把TodoList改为UI组件-提高性能

可以看到,现在的TodoList组件里没有任何的业务逻辑,只有一个Render方法,这时候就可以把它改为UI组件(无状态组件),UI组件就是一个方法,减少很多冗余操作,从而提高程序运行性能。这时候重新声明一个TodoList的变量,然后把render函数里的东西复制过来,只要稍加修改,就可以得到下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

const TodoList =(props)=>{
let {inputValue ,inputChange,clickButton,list} = props; // 粘贴过来后,此处要进行修改
return (
<div>
<div>
<input value={inputValue} onChange={inputChange} />
<button onClick={clickButton}>提交</button>
</div>
<ul>
{
list.map((item,index)=>{
return (<li key={index}>{item}</li>)
})
}
</ul>
</div>
);
}

代码写完后,我们删除一些不用的引入,然后就可以到浏览器中进行预览了。

1
2
3
4

import React from 'react';
import {connect} from 'react-redux'

目前TodoList.js的所有代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import React from 'react';
import {connect} from 'react-redux'


const TodoList =(props)=>{
let {inputValue ,inputChange,clickButton,list} = props; // 粘贴过来后,此处要进行修改
return (
<div>
<div>
<input value={inputValue} onChange={inputChange} />
<button onClick={clickButton}>提交</button>
</div>
<ul>
{
list.map((item,index)=>{
return (<li key={index}>{item}</li>)
})
}
</ul>
</div>
);
}



const stateToProps = (state)=>{
return {
inputValue : state.inputValue,
list:state.list
}
}

const dispatchToProps = (dispatch) =>{
return {
inputChange(e){
let action = {
type:'change_input',
value:e.target.value
}
dispatch(action)
},
clickButton(){
let action = {
type:'add_item'
}
dispatch(action)
}
}
}
export default connect(stateToProps,dispatchToProps)(TodoList);

那我们反过来,再来理解一下最后一句话代码的意思。

1
export default connect(stateToProps,dispatchToProps)(TodoList);

connect的作用是把UI组件(无状态组件)和业务逻辑代码的分开,然后通过connect再链接到一起,让代码更加清晰和易于维护。这也是React-Redux最大的有点。Redux的教程和视频到这里就结束了,下套课程我会讲解React-router,请小伙伴们持续关注博客.

redux和react-redux关系图

图片转载于https://blog.csdn.net/qq_42767631/article/details/83096841