React之Redux

知识点

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里重新获取数据,这样组件之间的数据传递变得容易很多。

在这里插入图片描述

  • 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该如何修改数据,比作告知管理员图书的存放地点。
    在这里插入图片描述
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.jsreducer.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一栏。
    在这里插入图片描述

  • 查看到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栏目,如下图
在这里插入图片描述

2)使用redux Flow流程
  • 获取store管理的数据
this.state = store.getState();
  • 创建一个修改数据的请求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()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值