天天看点

Reac.js入门Redux05

知识点

1、Redux概念简述

2、Redux的工作流程

3、使用Antd实现TodoList页面布局

4、创建Redux中的store

5、Action和Reducer的编写

6、使用Redux完成TodoList删除功能

7、ActionTypes的拆分

8、使用actionCreator统一创建action

9、Redux知识点复习补充

1、Redux概念简述

  • 由于React是一个视图层框架,如果需要进行跨层组件之间的通信,就会很复杂,很多代码变得不可维护,所以使用React来开发时,一般需要配合使用一个数据层框架,来辅助组件间的通信。
  • Redux是一个数据层框架,它通过将所有数据存储一个公用store空间里,而不把数据放在组件自身。如果一个组件修改数据,则只需要修改store里面的数据,然后其他组件会自动感知到store的变化,然后就从store里重新获取数据,这样组件之间的数据传递变得容易很多。
    Reac.js入门Redux05
  • Redux = Reducer + Flux,其中Flux是Facbook推出的最原始的辅助React的数据层框架,但是有一些不足,在公共存储区域Store可以由多个区域Store组成,这样存储时可能存在数据依赖问题,因此经过升级后得到Redux。Redux除了借鉴Flux的设计理念,还引入了Reducer的概念。

2、Redux的工作流程

  • React Components

    指页面上的组件,比作图书馆的借书人;
  • Action Creators

    指想要改变数据的请求,比作借书的请求;
  • Store

    指存储数据的公共区域,比作图书馆管理员;
  • React Components

    告知管理员

    Store

    有想要修改数据的意愿

    Action Creators

  • Reducers

    指如何修改数据的方法,比作图书馆书籍存放的记录本;
  • Reducers

    告知

    Store

    该如何修改数据,比作告知管理员图书的存放地点。
    Reac.js入门Redux05

3、使用Antd实现TodoList页面布局

1)Antd介绍

官方Antd文档 https://ant.design/docs/react/introduce-cn

简介:Antd 是基于 Ant Design 设计体系的 React UI 组件库,主要用于研发企业级中后台产品。

特性:

  • 提炼自企业级中后台产品的交互语言和视觉风格。
  • 开箱即用的高质量 React 组件。
  • 使用 TypeScript 构建,提供完整的类型定义文件。
  • 全链路开发和设计工具体系。

支持环境:现代浏览器和 IE9 及以上(需要 polyfills);支持服务端渲染;Electron。

2)Antd的引入

安装Antd包,打开cmd,输入以下命令行。安装完后,重启服务器。

cd <项目目录下>
$ npm install antd --save  
$ npm run start 
           
3)使用Antd布局TodoList

TodoList.js(重写TodoList)

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



// 使用Antd实现TodoList布局


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={{margin:'10px'}}>
				<div>
					<Input placeholder='todo info' style={{width:'300px',marginRight:'10px'}} />
					 <Button type="primary">提交</Button>
				</div>
				<List
					style={{width:'300px',marginTop:'10px'}}
				    bordered
				    dataSource={data}
				    renderItem={item => (<List.Item>{item}</List.Item>)}
			    />
			</div>
		)
	}
}

export default TodoList;
           

4、创建Redux中的store

官网redux文档 https://redux.js.org/introduction/getting-started

创建一个store进行数据管理,比作图书馆管理员

1)安装redux

安装Redux包,打开cmd,输入以下命令行。安装完后,重启服务器。

cd <项目目录下>
$ npm install redux --save  
$ npm run start 
           
2)创建并引入store
  • 在项目

    learn-react -> src

    文件夹下,创建一个

    store

    文件夹,并且创建两个

    js

    文件,分别为

    index.js

    reducer.js

  • index.js

    表示一个数据管理员;
  • reducer.js

    表示一个数据记录本;
  • TodoList.js

    跟管理员store获取数据

    this.state = store.getState();

src/store/index.js(新建js文件)

import {
	createStore
} from 'redux';
import reducer from './reducer'; //引入记事本


// 创建一个图书管理员store
const store = createStore(reducer);

export default store;
           

src/store/reducer.js(新建js文件)

// 创建记事本,记录数据信息
// state存储整个图书馆的全部信息

const defaultState = {
	inputValue: '123',
	list: [1, 23, 4]
};

// 返回数据
export default (state = defaultState, action) => {
	return state;
}
           

src/TodoList.js(修改TodoList)

import React, {
	Component
} from 'react';
import 'antd/dist/antd.css';
import {
	Input,
	Button,
	List
} from 'antd';
import store from './store'; //引入管理员



// 使用Antd实现TodoList布局


class TodoList extends Component {

	constructor(props) {
		super(props);
		// 获取store的数据
		this.state = store.getState();
	}

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

export default TodoList;
           

5、Action和Reducer的编写

1)安装redux devtools
  • 打开翻墙软件 -> 点击谷歌浏览器右上角三点处 -> 选择“更多工具 - 扩展程序” -> 点击“扩展程序”页面左上角”扩展程序“ -> 点击展开的侧边栏右下角的“打开Chrome网上应用店” -> 在应用店页面搜索 " redux devtools " -> 点击搜索结果 " redux devtools " 的 " 添加至Chrome " -> 添加成功后,可以在页面右上角看到redux图标 -> 重启浏览器,就可以在开发者工具看到redux一栏。
    Reac.js入门Redux05
    Reac.js入门Redux05
Reac.js入门Redux05
  • 查看到redux栏目显示 Not Found,因此需要在

    src/store/index.js

    文件中

    createStore()

    添加一个参数,具体如下:

    src/store/index.js(修改js文件)

    ...
    const store = createStore(
    	reducer,
    	window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
    );
    ...
               
    修改完后,重新查看redux栏目,如下图
    Reac.js入门Redux05
2)使用redux Flow流程
  • 获取store管理的数据
  • 创建一个修改数据的请求Action Creators,修改Input框的输入内容,给Input框增加一个change事件;
    <Input
    	value={this.state.inputValue} 
    	placeholder='todo info' 
    	style={{width:'300px',marginTop:'10px',marginRight:'10px'}} 
    	onChange={this.handleInputChange}
    />
               
  • 告知store这个请求dispatch action,store会自动将之前的数据previousState和当前用户想要的操作action自动转发给reducer;
    handleInputChange(e) {
    	// 创建一个请求
    	 const action = {
    				type: 'change_input_value',
    				value: e.target.value
    			};
    	// 告知store这个请求
    	 store.dispatch(action);
    }
               
  • reducers进行数据修改,并将修改后的新数据返回给store;
    export default (state = defaultState, action) => {
    	if (action.type === 'change_input_value') {
    			// 对旧数据进行深拷贝
    			const newState = JSON.parse(JSON.stringify(state));
    			// 修改新数据并返回
    			newState.inputValue = action.value;
    			return newState;
    		}
    	console.log(state, action);
    	return state;
    }
               
  • store会自动地将reducer处理后返回的新数据替换掉旧数据
    const store = createStore(
    	reducer,
    	window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
    );
               
  • TodoList组件识别到store的数据变化,就自动更新组件的state数据,将Input框输入的新内容显示到框内,到此就完成一个Redux Flow流程。
    ...
    	this.handleStoreChange = this.handleStoreChange.bind(this);
    	// 声明一个函数,这个函数当store的数据一旦改变,就可自动的执行
    	store.subscribe(this.handleStoreChange);
    ...
    	handleStoreChange() {
    		//更新组件数据
    		this.setState(store.getState());
    	}
    ...
    
               
  • Input框输入内容点击提交,修改store数据的 list 字段,给按钮添加点击事件

    handleBtnClick()

    ,发起一个action,并告知store。
    ...
    <Button type="primary" onClick={this.handleBtnClick}>提交</Button>
    ...
    handleBtnClick() {
    	const action = {
    			type: 'add_todo_item',
    		}
    	store.dispatch(action);
    }
    ...
               
  • store得到action请求后,自动将previousState和action传递给reducer,reducer进行数据处理后,将新数据newState返回给store。
    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;
    }
               
  • store感知到数据变化后,会自动执行

    handleStoreChange()

    函数,从而将提交的内容显示到页面上。

完整代码修改

src/TodoList.js(修改js文件)

import React, {
	Component
} from 'react';
import 'antd/dist/antd.css';
import {
	Input,
	Button,
	List
} from 'antd';
import store from './store'; //引入管理员



// 使用Antd实现TodoList布局


class TodoList extends Component {

	constructor(props) {
		super(props);
		// 1、获取store管理的数据
		this.state = store.getState();
		this.handleInputChange = this.handleInputChange.bind(this);
		this.handleStoreChange = this.handleStoreChange.bind(this);
		this.handleBtnClick = this.handleBtnClick.bind(this);
		// 6、从store获取数据,更新组件state数据
		// 声明一个函数,这个函数当store的数据一旦改变,就可自动的执行
		store.subscribe(this.handleStoreChange);
	}

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

	handleInputChange(e) {
		// 更改store管理的数据

		//2、创建一个请求
		const action = {
			type: 'change_input_value',
			value: e.target.value
		};
		//3、告知store这个请求,store会自动将之前的数据previousState和当前用户想要的操作action自动转发给reducer
		store.dispatch(action);
	}

	handleStoreChange() {
		//更新组件数据
		this.setState(store.getState());
	}

	// 7、点击提交input框内容
	handleBtnClick() {
		const action = {
			type: 'add_todo_item',
		}
		store.dispatch(action);
	}
}

export default TodoList;
           

src/store/index.js(修改js文件)

import {
	createStore
} from 'redux';
import reducer from './reducer'; //引入笔记本


// 创建一个图书管理员store
// 5、store会自动地将reducer处理后返回的新数据替换掉旧数据
const store = createStore(
	reducer,
	window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);

export default store;
           

src/store/reducer.js(修改js文件)

// 创建记事本,记录数据信息

const defaultState = {
	inputValue: '123',
	list: [1, 23, 4]
};

// 返回数据
// state存储整个图书馆的全部信息
// reducer可以接受state,但绝不能修改state
export default (state = defaultState, action) => {
	// 4、告知store如何处理change_input_value请求
	if (action.type === 'change_input_value') {
		// 对旧数据进行深拷贝
		const newState = JSON.parse(JSON.stringify(state));
		// 修改新数据并返回
		newState.inputValue = action.value;
		return newState;
	}

	// 8、告知store如何处理add_todo_item请求
	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;
}
           

6、使用Redux完成TodoList删除功能

给列表 list 的每一项 item 绑定一个点击删除事件

src/TodoList.js(添加代码)

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

src/store/reducer.js(添加代码)

...
	if (action.type === 'delete_todo_item') {
		const newState = JSON.parse(JSON.stringify(state));
		newState.list.splice(action.index, 1);
		return newState;
	}
...
           

7、ActionTypes的拆分

如果把action的type的值写错,比如TodoList.js文件中把

change_input_value

写成

change_invt_value

,到页面进行调试,发现控制台并不会报错。所以,为了更好的方便调试,将action的type名抽取出来。因此,在 src / store 文件夹下,新建一个 actionTypes.js 文件,进行记录action的Type名称。

代码修改如下

src/TodoList.js(修改代码)

...
import {
	CHANGE_INPUT_VALUE,
	ADD_TODO_ITEM,
	DELETE_TODO_ITEM
} from './store/actionTypes';
...
		const action = {
			type: CHANGE_INPUT_VALUE,
			value: e.target.value
		};
...
		const action = {
			type: ADD_TODO_ITEM,
		};
...
		const action = {
			type: DELETE_TODO_ITEM,
			index
		};

           

src/store/reducer.js(修改代码)

import {
	CHANGE_INPUT_VALUE,
	ADD_TODO_ITEM,
	DELETE_TODO_ITEM
} from './actionTypes';
...
	if (action.type === CHANGE_INPUT_VALUE) {...
...
	if (action.type === ADD_TODO_ITEM) {...
...
	if (action.type === DELETE_TODO_ITEM) {...
...
           

src/store/actionTypes.js(新建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';
           

8、使用actionCreator统一创建action

为了便于管理

action

,将所有

action

放到

actionCreator

进行统一管理。作用提高代码的可维护性,方便前端做代码的自动化测试。因此,在 src / store 文件夹下,新建一个 actionCreators.js 文件,统一管理action。

代码修改如下

src/TodoList.js(修改代码)

...
// import {
// 	CHANGE_INPUT_VALUE,
// 	ADD_TODO_ITEM,
// 	DELETE_TODO_ITEM
// } from './store/actionTypes';
//删除以上注释代码,修改为以下代码
import {
	getInputChangeAction,
	getAddItemAction,
	getDeleteItemAction
} from './store/actionCreators';
...
		//const action = {
		//	type: CHANGE_INPUT_VALUE,
		//	value: e.target.value
		//};
		//删除以上注释代码,修改为以下代码
		const action = getInputChangeAction(e.target.value);
...
		//const action = {
		//	type: ADD_TODO_ITEM,
		//};
		//删除以上注释代码,修改为以下代码
		const action = getAddItemAction();
...
		//const action = {
		//	type: DELETE_TODO_ITEM,
		//	index
		//};
		//删除以上注释代码,修改为以下代码
		const action = getDeleteItemAction(index);
           

src/store/actionCreators.js(新建js文件)

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

//返回一个js对象
export const getInputChangeAction = (value) => ({
	type: CHANGE_INPUT_VALUE,
	value
});

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

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

9、Redux知识点复习补充

1)Redux设计和使用的三项原则
  • store是唯一的
  • 只有 store 能够改变自己的内容。表面上看,像 reducer 在修改 state 数据,但实际上,reducer 经过处理数据之后,将新的 newState 返回给 store ,store收到 reducer 返回的新newState,然后对自己的旧数据进行更新,而 reducer 并不能更改 store 的数据。所以,不能在 reducer 中直接写

    state.list = ...

    ,因为这样就在 reducer 中直接更改了store的数据,所以 reducer 在进行数据修改时,必须要先深拷贝一份新数据出来进行修改。
  • Reducer 必须是一个纯函数。纯函数,指的是给定固定的输入,就一定会有固定的输出,而且不会有任何副作用。一旦一个函数内部有

    new Date()、setTimeout()、ajax()

    等函数,这些函数会因时间变化而内容变化,那么这个函数就不是纯函数。加入在这个函数内部加入

    state.inputValue=action.value;

    ,这句话会造成修改store的数据,一旦修改了,那么就说明带来了额外的副作用,那么这个函数就不是纯函数。
2)Redux核心API
  • createStore()
  • store.dispatch()
  • store.getState()
  • store.subscribe()