redux学习笔记

学习参考链接

中文官方文档:http://cn.redux.js.org/
阮一峰教程:http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_one_basic_usages.html

基础知识点:

React,状态数据,state
组件与组件之间可以传递数据: props、回传事件
兄弟之间传递数据:共同的子元素或者共同的父元素(大量的组件需要视同同一条数据)
如果你不知道什么时候需要使用Redux,就是你不需要使用它,当你遇到解决不了的问题,自然回想起Redux

父子组件传递:

Parent.jsx

import React from 'react';
import Child from './Child';

export default class Parent extends React.Component {
	state = {
		value: '',
	};

	clickHandle = (data) => {
		console.log(data);
		this.setState({
			value: data,
		});
	};

	render() {
		return (
			<div>
				Parent: {this.state.value}
				<Child title="父传子:子标题" onMyEvent={this.clickHandle} />
			</div>
		);
	}
}

child.jsx

import React from 'react';

export default class Child extends React.Component {
	clickHandle = (e) => {
		this.props.onMyEvent('子组件传递数据给父组件:父标题');
	};

	render() {
		return (
			<div>
				Child: {this.props.title}
				<button onClick={this.clickHandle}>
					子组件传递数据给父组件
				</button>
			</div>
		);
	}
}
redux和react-redux的区别
    redux: js的状态管理 createStore
    react-redux: 为了在react煮给你容易的使用:connect provider
1.安装
    npm install --save-dev redux

简单实现

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { createStore } from 'redux';
import reducer from './reducers/counter';

// 创建store仓库
const store = createStore(reducer);
// 监听数据变化
store.subscribe(() => console.log('state:', store.getState()));

const render = () => {
	ReactDOM.render(
		<React.StrictMode>
			<App
				onIncrement={() => store.dispatch({ type: 'INCREMENT' })}
				onDecrement={() => store.dispatch({ type: 'DECREMENT' })}
				// 提交渲染
				value={store.getState()}
			/>
		</React.StrictMode>,
		document.getElementById('root')
	);
};

render();
store.subscribe(render);

counter.js

const counter = (state = 0, action) => {
	switch (action.type) {
		case 'INCREMENT':
			return state + 1;
		case 'DECREMENT':
			return state - 1;
		default:
			return state;
	}
};

export default counter;

App.js

import React from 'react';
// import Parent from './components/coms/Parent';

export default class extends React.Component {
	render() {
		return (
			<div className="App">
				{/* <Parent /> */}
				<h1 className="jumbotron-heading text-center">
					{this.props.value}
				</h1>
				<p className="text-center">
					<button
						onClick={this.props.onIncrement}
						className="btn btn-primary"
					>
						increment
					</button>
					<button
						onClick={this.props.onDecrement}
						className="btn btn-success"
					>
						decrement
					</button>
				</p>
			</div>
		);
	}
}

Redux引入react-redux与mapStateToProps读取数据与dispatch、mapDispatchToProps修改数据

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { createStore } from 'redux';
import reducer from './reducers/counter';
import { Provider } from 'react-redux';

// 创建store仓库
const store = createStore(reducer);

ReactDOM.render(
	<React.StrictMode>
		<Provider store={store}>
			<App />
		</Provider>
	</React.StrictMode>,
	document.getElementById('root')
);

App.js

import React from 'react';
// import Parent from './components/coms/Parent';
import { connect } from 'react-redux';
import { increment, decrement } from './actions/counter';

class App extends React.Component {
	render() {
		console.log(this.props);
		const { increment, decrement } = this.props;
		return (
			<div className="container">
				{/* <Parent /> */}
				<h1 className="jumbotron-heading text-center">
					{this.props.counter}
				</h1>
				<p className="text-center">
					<button
						onClick={() => increment()}
						className="btn btn-primary"
					>
						increment
					</button>
					<button
						onClick={() => decrement()}
						className="btn btn-success"
					>
						decrement
					</button>
				</p>
			</div>
		);
	}
}

// 读取数据的方案
const mapStateToProps = (state) => {
	return {
		counter: state,
	};
};

const mapDispatchToProps = (dispatch) => {
	return {
		increment: () => {
			dispatch(increment());
		},
		decrement: () => {
			dispatch(decrement());
		},
	};
};

// 高阶组件关联写法
// mapStateToProps,mapDispatchToProps,这两个的先后顺序不能颠倒
export default connect(mapStateToProps, mapDispatchToProps)(App);

counter.js(reducers下的counter.js)

const counter = (state = 0, action) => {
	switch (action.type) {
		case 'INCREMENT':
			return state + 1;
		case 'DECREMENT':
			return state - 1;
		default:
			return state;
	}
};

export default counter;

counter.js(actions下的counter.js)

export function increment() {
	return {
		type: 'INCREMENT',
	};
}

export function decrement() {
	return {
		type: 'DECREMENT',
	};
}

App.js中更加完美的写法

import React from 'react';
// import Parent from './components/coms/Parent';
import { connect } from 'react-redux';
// import { increment, decrement } from './actions/counter';
import * as counterActions from './actions/counter';
import { bindActionCreators } from 'redux';

class App extends React.Component {
	render() {
		console.log(this.props);
		// const { increment, decrement } = this.props;
		return (
			<div className="container">
				{/* <Parent /> */}
				<h1 className="jumbotron-heading text-center">
					{this.props.counter}
				</h1>
				<p className="text-center">
					{/* <button
						onClick={this.props.onIncrement}
						className="btn btn-primary"
					>
						increment
					</button>
					<button
						onClick={this.props.onDecrement}
						className="btn btn-success"
					>
						decrement
					</button> */}
					{/* <button
						onClick={() => increment()}
						className="btn btn-primary"
					>
						increment
					</button>
					<button
						onClick={() => decrement()}
						className="btn btn-success"
					>
						decrement
					</button> */}
					<button
						onClick={() => this.props.counterActions.increment()}
						className="btn btn-primary"
					>
						increment
					</button>
					<button
						onClick={() => this.props.counterActions.decrement()}
						className="btn btn-success"
					>
						decrement
					</button>
				</p>
			</div>
		);
	}
}

// 读取数据的方案
const mapStateToProps = (state) => {
	return {
		counter: state,
	};
};

// const mapDispatchToProps = (dispatch) => {
// 	return {
// 		increment: () => {
// 			dispatch(increment());
// 		},
// 		decrement: () => {
// 			dispatch(decrement());
// 		},
// 	};
// };

const mapDispatchToProps = (dispatch) => {
	return {
		counterActions: bindActionCreators(counterActions, dispatch),
	};
};

// 高阶组件关联写法
// mapStateToProps,mapDispatchToProps,这两个的先后顺序不能颠倒
export default connect(mapStateToProps, mapDispatchToProps)(App);

Redux bindActionCreators与参数传递

新建一个存储常量文件constants/index.js
constants/index.js

// counter
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';

actions/counter.js

import * as actions from '../constants';

export function increment(num) {
	return {
		type: actions.INCREMENT,
		num: num,
	};
}

export function decrement(num) {
	return {
		type: actions.DECREMENT,
		num: num,
	};
}

reducers/counter.js

import * as actions from '../constants';

const counter = (state = 0, action) => {
	switch (action.type) {
		case actions.INCREMENT:
			return state + action.num;
		case actions.DECREMENT:
			return state - action.num;
		default:
			return state;
	}
};

export default counter;

App.js

import React from 'react';
// import Parent from './components/coms/Parent';
import { connect } from 'react-redux';
// import { increment, decrement } from './actions/counter';
import * as counterActions from './actions/counter';
import { bindActionCreators } from 'redux';

class App extends React.Component {
	render() {
		console.log(this.props);
		// const { increment, decrement } = this.props;
		return (
			<div className="container">
				{/* <Parent /> */}
				<h1 className="jumbotron-heading text-center">
					{this.props.counter}
				</h1>
				<p className="text-center">
					{/* <button
						onClick={this.props.onIncrement}
						className="btn btn-primary"
					>
						increment
					</button>
					<button
						onClick={this.props.onDecrement}
						className="btn btn-success"
					>
						decrement
					</button> */}
					{/* <button
						onClick={() => increment()}
						className="btn btn-primary"
					>
						increment
					</button>
					<button
						onClick={() => decrement()}
						className="btn btn-success"
					>
						decrement
					</button> */}
					<button
						onClick={() => this.props.counterActions.increment(10)}
						className="btn btn-primary"
					>
						increment
					</button>
					<button
						onClick={() => this.props.counterActions.decrement(5)}
						className="btn btn-success"
					>
						decrement
					</button>
				</p>
			</div>
		);
	}
}

// 读取数据的方案
const mapStateToProps = (state) => {
	return {
		counter: state,
	};
};

// const mapDispatchToProps = (dispatch) => {
// 	return {
// 		increment: () => {
// 			dispatch(increment());
// 		},
// 		decrement: () => {
// 			dispatch(decrement());
// 		},
// 	};
// };

const mapDispatchToProps = (dispatch) => {
	return {
		counterActions: bindActionCreators(counterActions, dispatch),
	};
};

// 高阶组件关联写法
// mapStateToProps,mapDispatchToProps,这两个的先后顺序不能颠倒
export default connect(mapStateToProps, mapDispatchToProps)(App);

Redux combineReducers合并reducer

添加user组件:components/user.jsx

import React from 'react';

export default class User extends React.Component {
	render() {
		return (
			<div className="continer">
				<p className="text-center">User</p>
			</div>
		);
	}
}

导出常量ADD_USER:constants/index.js

// counter
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';

// user
export const ADD_USER = 'ADD_USER';

actions/user.js

import { ADD_USER } from '../constants';

export function addUser() {
	return {
		type: ADD_USER,
	};
}

reducers/user.js

import { ADD_USER } from '../constants';

const user = (state = {}, action) => {
	switch (action.type) {
		case ADD_USER:
			state.push('iwen');
			return state;
		default:
			return state;
	}
};

export default user;

新建合并reducers文件:reducers/index.js

import { combineReducers } from 'redux';
import counter from './counter';
import user from './user';

const rootReducer = combineReducers({
	counter: counter,
	user: user,
});

export default rootReducer;

引入rootReducer

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { createStore } from 'redux';
// import reducer from './reducers/counter';
import { Provider } from 'react-redux';
import rootReducer from './reducers';

// 创建store仓库
const store = createStore(rootReducer);
// 监听数据变化
// store.subscribe(() => console.log('state:', store.getState()));

// const render = () => {
// 	ReactDOM.render(
// 		<React.StrictMode>
// 			<App
// 				onIncrement={() => store.dispatch({ type: 'INCREMENT' })}
// 				onDecrement={() => store.dispatch({ type: 'DECREMENT' })}
// 				// 提交渲染
// 				value={store.getState()}
// 			/>
// 		</React.StrictMode>,
// 		document.getElementById('root')
// 	);
// };

// render();
// store.subscribe(render);

ReactDOM.render(
	<React.StrictMode>
		<Provider store={store}>
			<App />
		</Provider>
	</React.StrictMode>,
	document.getElementById('root')
);

在App.js中引入,并引入bindActionCreators来合并reducer

import React from 'react';
// import Parent from './components/coms/Parent';
import { connect } from 'react-redux';
// import { increment, decrement } from './actions/counter';
import * as counterActions from './actions/counter';
import { bindActionCreators } from 'redux';
import User from './components/user';

class App extends React.Component {
	render() {
		console.log(this.props);
		// const { increment, decrement } = this.props;
		return (
			<div className="container">
				{/* <Parent /> */}
				<h1 className="jumbotron-heading text-center">
					{this.props.counter}
				</h1>
				<p className="text-center">
					{/* <button
						onClick={this.props.onIncrement}
						className="btn btn-primary"
					>
						increment
					</button>
					<button
						onClick={this.props.onDecrement}
						className="btn btn-success"
					>
						decrement
					</button> */}
					{/* <button
						onClick={() => increment()}
						className="btn btn-primary"
					>
						increment
					</button>
					<button
						onClick={() => decrement()}
						className="btn btn-success"
					>
						decrement
					</button> */}
					<button
						onClick={() => this.props.counterActions.increment(10)}
						className="btn btn-primary"
					>
						increment
					</button>
					<button
						onClick={() => this.props.counterActions.decrement(5)}
						className="btn btn-success"
					>
						decrement
					</button>
				</p>
				<User />
			</div>
		);
	}
}

// 读取数据的方案
const mapStateToProps = (state) => {
	console.log(state);
	return {
		// counter: state,
		counter: state.counter,
	};
};

// const mapDispatchToProps = (dispatch) => {
// 	return {
// 		increment: () => {
// 			dispatch(increment());
// 		},
// 		decrement: () => {
// 			dispatch(decrement());
// 		},
// 	};
// };

const mapDispatchToProps = (dispatch) => {
	return {
		counterActions: bindActionCreators(counterActions, dispatch),
	};
};

// 高阶组件关联写法
// mapStateToProps,mapDispatchToProps,这两个的先后顺序不能颠倒
export default connect(mapStateToProps, mapDispatchToProps)(App);

Redux中间件与第三方中间件logger

根目录下的index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { applyMiddleware, createStore } from 'redux';
// import reducer from './reducers/counter';
import { Provider } from 'react-redux';
import rootReducer from './reducers';

// 自己编写中间件
const logger = (store) => (next) => (action) => {
	console.log('dispath->', action);
	let result = next(action); // 加载下一个中间件
	console.log('next state->', store.getState());
	return result;
};

const error = (store) => (next) => (action) => {
	try {
		next(action);
	} catch (e) {
		console.log('error->', e);
	}
};

const store = createStore(rootReducer, {}, applyMiddleware(logger, error));

// 创建store仓库
// const store = createStore(rootReducer);
// 监听数据变化
// store.subscribe(() => console.log('state:', store.getState()));

// const render = () => {
// 	ReactDOM.render(
// 		<React.StrictMode>
// 			<App
// 				onIncrement={() => store.dispatch({ type: 'INCREMENT' })}
// 				onDecrement={() => store.dispatch({ type: 'DECREMENT' })}
// 				// 提交渲染
// 				value={store.getState()}
// 			/>
// 		</React.StrictMode>,
// 		document.getElementById('root')
// 	);
// };

// render();
// store.subscribe(render);

ReactDOM.render(
	<React.StrictMode>
		<Provider store={store}>
			<App />
		</Provider>
	</React.StrictMode>,
	document.getElementById('root')
);

第三方中间件
先下载安装:npm install --save-dev redux-logger
然后在根目录下的index.js中引入redux-logger

根目录index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { applyMiddleware, createStore } from 'redux';
import logger from 'redux-logger';
// import reducer from './reducers/counter';
import { Provider } from 'react-redux';
import rootReducer from './reducers';

// // 自己编写中间件
// const logger = (store) => (next) => (action) => {
// 	console.log('dispath->', action);
// 	let result = next(action); // 加载下一个中间件
// 	console.log('next state->', store.getState());
// 	return result;
// };

// const error = (store) => (next) => (action) => {
// 	try {
// 		next(action);
// 	} catch (e) {
// 		console.log('error->', e);
// 	}
// };

// const store = createStore(rootReducer, {}, applyMiddleware(logger,error));

// 第三方中间件
const store = createStore(rootReducer, {}, applyMiddleware(logger));

// 创建store仓库
// const store = createStore(rootReducer);
// 监听数据变化
// store.subscribe(() => console.log('state:', store.getState()));

// const render = () => {
// 	ReactDOM.render(
// 		<React.StrictMode>
// 			<App
// 				onIncrement={() => store.dispatch({ type: 'INCREMENT' })}
// 				onDecrement={() => store.dispatch({ type: 'DECREMENT' })}
// 				// 提交渲染
// 				value={store.getState()}
// 			/>
// 		</React.StrictMode>,
// 		document.getElementById('root')
// 	);
// };

// render();
// store.subscribe(render);

ReactDOM.render(
	<React.StrictMode>
		<Provider store={store}>
			<App />
		</Provider>
	</React.StrictMode>,
	document.getElementById('root')
);

Redux异步中间件

actions/counter.js

import * as actions from '../constants';

// export function increment(num) {
// 	return {
// 		type: actions.INCREMENT,
// 		num: num,
// 	};
// }

export function increment(num) {
	return (dispatch) => {
		setTimeout(() => {
			dispatch({
				type: actions.INCREMENT,
				num: num,
			});
		}, 1000);
	};
}

export function decrement(num) {
	return {
		type: actions.DECREMENT,
		num: num,
	};
}

下载安装异步中间件: npm install --save-dev redux-thunk
在根目录下的index.js中引入

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { applyMiddleware, createStore } from 'redux';
import logger from 'redux-logger';
import thunk from 'redux-thunk';
// import reducer from './reducers/counter';
import { Provider } from 'react-redux';
import rootReducer from './reducers';

// // 自己编写中间件
// const logger = (store) => (next) => (action) => {
// 	console.log('dispath->', action);
// 	let result = next(action); // 加载下一个中间件
// 	console.log('next state->', store.getState());
// 	return result;
// };

// const error = (store) => (next) => (action) => {
// 	try {
// 		next(action);
// 	} catch (e) {
// 		console.log('error->', e);
// 	}
// };

// const store = createStore(rootReducer, {}, applyMiddleware(logger,error));

// 第三方中间件
const store = createStore(rootReducer, {}, applyMiddleware(logger, thunk));

// 创建store仓库
// const store = createStore(rootReducer);
// 监听数据变化
// store.subscribe(() => console.log('state:', store.getState()));

// const render = () => {
// 	ReactDOM.render(
// 		<React.StrictMode>
// 			<App
// 				onIncrement={() => store.dispatch({ type: 'INCREMENT' })}
// 				onDecrement={() => store.dispatch({ type: 'DECREMENT' })}
// 				// 提交渲染
// 				value={store.getState()}
// 			/>
// 		</React.StrictMode>,
// 		document.getElementById('root')
// 	);
// };

// render();
// store.subscribe(render);

ReactDOM.render(
	<React.StrictMode>
		<Provider store={store}>
			<App />
		</Provider>
	</React.StrictMode>,
	document.getElementById('root')
);

Redux-thunk实现网络请求

constants/index.js

// counter
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';

// user
export const FETCH_USER_SUCCESS = 'FETCH_USER_SUCCESS';

/reducers/user.js

import { FETCH_USER_SUCCESS } from '../constants';

const initialState = {
	user: {},
};

const user = (state = initialState, action) => {
	switch (action.type) {
		case FETCH_USER_SUCCESS:
			// 三大原则之一,State 是只读的,唯一改变state的方法是触发acction,action是一个用于描述已发生事件的普通对象
			return {
				user: action.user,
			};
		default:
			return state;
	}
};

export default user;

actions/user.js

import { FETCH_USER_SUCCESS } from '../constants';

export function fetch_user(user) {
	return {
		type: FETCH_USER_SUCCESS,
		user,
	};
}

export const get_user = () => {
	return (dispatch) => {
		fetch('http://iwenwiki.com/api/blueberrypai/getChengpinDetails.php')
			.then((res) => res.json())
			.then((data) => {
				console.log(data);
				dispatch(fetch_user(data.chengpinDetails[0]));
			})
			.catch((error) => {
				console.log(error);
			});
	};
};

components/user.jsx

import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as userActions from '../actions/user';

class User extends React.Component {
	render() {
		console.log(this.props.user);

		return (
			<div className="continer text-center">
				<p className="text-center">{this.props.user.user.title}</p>
				<p className="text-center">User</p>
				<button
					className="btn btn-primary"
					onClick={() => {
						this.props.userActions.get_user();
					}}
				>
					getUser
				</button>
			</div>
		);
	}
}

const mapStateToProps = (state) => {
	return {
		user: state.user,
	};
};

const mapDispatchToProps = (dispatch) => {
	return {
		userActions: bindActionCreators(userActions, dispatch),
	};
};

export default connect(mapStateToProps, mapDispatchToProps)(User);

Redux-thunk网络请求的三种状态

constants/index.js

// counter
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';

// user
export const FETCH_USER_SUCCESS = 'FETCH_USER_SUCCESS';
export const FETCH_USER_REQUEST = 'FETCH_USER_REQUEST';
export const FETCH_USER_FAILURE = 'FETCH_USER_FAILURE';

reducers/user.js

import {
	FETCH_USER_FAILURE,
	FETCH_USER_REQUEST,
	FETCH_USER_SUCCESS,
} from '../constants';

const initialState = {
	isFetching: false,
	error: null,
	user: {},
};

const user = (state = initialState, action) => {
	switch (action.type) {
		case FETCH_USER_SUCCESS:
			// 三大原则之一,State 是只读的,唯一改变state的方法是触发acction,action是一个用于描述已发生事件的普通对象
			return {
				isFetching: false,
				error: null,
				user: action.user,
			};
		case FETCH_USER_REQUEST:
			return {
				isFetching: true,
				error: null,
				user: {},
			};
		case FETCH_USER_FAILURE:
			return {
				isFetching: false,
				error: action.error,
				user: {},
			};
		default:
			return state;
	}
};

export default user;

actions/user.js

import {
	FETCH_USER_FAILURE,
	FETCH_USER_SUCCESS,
	FETCH_USER_REQUEST,
} from '../constants';

export const fetch_user_failure = (error) => {
	return {
		type: FETCH_USER_FAILURE,
		error,
	};
};

export const fetch_user = (user) => {
	return {
		type: FETCH_USER_SUCCESS,
		user,
	};
};

export const fetch_user_request = (error) => {
	return {
		type: FETCH_USER_REQUEST,
	};
};

export const get_user = () => {
	return (dispatch) => {
		dispatch(fetch_user_request());
		fetch('http://iwenwiki.com/api/blueberrypai/getChengpinDetails.php')
			.then((res) => res.json())
			.then((data) => {
				console.log(data);
				dispatch(fetch_user(data.chengpinDetails[0]));
			})
			.catch((error) => {
				dispatch(fetch_user_failure(error));
			});
	};
};

components/user.jsx

import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as userActions from '../actions/user';

class User extends React.Component {
	render() {
		console.log(this.props.user);

		const { error, isFetching, user } = this.props.user;

		let data;
		if (error) {
			data = error;
		} else if (isFetching) {
			console.log(isFetching);
			data = 'Loading......';
		} else {
			data = user.title;
		}

		return (
			<div className="continer text-center">
				<p className="text-center">{data}</p>
				<p className="text-center">User</p>
				<button
					className="btn btn-primary"
					onClick={() => {
						this.props.userActions.get_user();
					}}
				>
					getUser
				</button>
			</div>
		);
	}
}

const mapStateToProps = (state) => {
	return {
		user: state.user,
	};
};

const mapDispatchToProps = (dispatch) => {
	return {
		userActions: bindActionCreators(userActions, dispatch),
	};
};

export default connect(mapStateToProps, mapDispatchToProps)(User);

持续更新中......

原文地址:https://www.cnblogs.com/lhongsen/p/14354801.html