学习参考链接
中文官方文档: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);
持续更新中......