例子来源于网上的视频教程,总结一下。
redux原理图
action
- 动作的对象
- 包含2个属性
type:标识属性,值为字符串,唯一,必要属性 - 例子:{type: ‘ADD_STUDENT’,data:{ name:‘tom’ , age:18 }}
reducer
- 用于初始化状态,加工状态
- 加工时,根据旧的state和action,产生新的state的纯函数
store
- 将state、action、reducer联系在一起
- 如何得到此对象?
(1)import {createStore} from ‘redux’
(2)import reducer from ‘./reducers’
(3)const store = createStore(reducer) - 此对象的功能
(1)getState():得到state
(2)dispatch():分发action,触发reducer调用,产生新的state
(3)subscribe():注册监听,当产生了新的state时,自动调用
精简版redux
下载redux npm i redux
- 创建action 通过redux里面的一个方法 createStore 把reducers传入
import {createStore} from 'redux';
import {countReducers} from './countReducers'
export default createStore(countReducers);
- 创建reducers 因为是一个计算的例子 所以起名为countReducers
export const countReducers = (preState=0,action) =>{
switch(action.type){
case 'add' :
return preState + action.data*1;
case 'sub' :
return preState - action.data*1;
default :
return preState
}
}
- 在App.js里面写入例子的相关代码
import React, { Component } from 'react';
import store from './redux/store';
export default class App extends Component {
state={
selectValue : 1
}
//加
add = () =>{
const {selectValue} = this.state;
store.dispatch({type:'add',data:selectValue});
}
//减
sub = () =>{
const {selectValue} = this.state;
store.dispatch({type:'sub',data:selectValue});
}
//异步加
asyncAdd = () =>{
const {selectValue} = this.state;
setTimeout(() => {
store.dispatch({type:'add',data:selectValue});
}, 1000);
}
//奇数加
oddAdd = () =>{
const {selectValue} = this.state;
const count = store.getState();
if(count%2===1){
store.dispatch({type:'add',data:selectValue});
}
}
render() {
return (
<div>
<h1>count:{store.getState()}</h1>
<select onChange={(e)=>this.setState({selectValue:e.target.value})}>
<option value='1'>1</option>
<option value='2'>2</option>
<option value='3'>3</option>
</select>
<button onClick={this.add}>+</button>
<button onClick={this.sub}>-</button>
<button onClick={this.asyncAdd}>异步加</button>
<button onClick={this.oddAdd}>奇数加</button>
</div>
);
}
}
- 因为redux是一个第三方库 它只能帮我们管理状态 但是不能像react里面的状态一样一旦改变就自动render 所以我们要用store.subscribe()来进行监听redux里面的状态,一旦改变进行实时的更新
import React from 'react';
import ReactDOM from 'react-dom'
import App from "./App";
import store from './redux/store';
ReactDOM.render(<App />,document.getElementById('root'));
store.subscribe(()=>{
ReactDOM.render(<App />,document.getElementById('root'));
})
精简版结束,里面没有涉及到actions因为action仅仅只是一个obj的对象。我们可以直接写一个对象替代,在后面的例子中我们会加入actions文件
redux完整版
和上一版的区别是增加了actions.js为我们生成action方法,在app.js发布方法不需要写对象了。直接调用actions.js里面的方法即可,为了防止因为马虎写错单词,我们可以新建一个constant.js文件来声明常量
- 创建constant.js存储常量
export const ADD = 'add'
export const SUB = 'sub'
- 创建actions.js用来创建action
import { ADD,SUB } from './constant';
export const add = (data) =>({type: ADD,data});
export const sub = (data) =>({type:SUB,data})
- 把字符串用声明好的常量代替
import { ADD,SUB } from './constant';
export const countReducers = (preState=0,action) =>{
switch(action.type){
case ADD :
return preState + action.data*1;
case SUB :
return preState - action.data*1;
default :
return preState
}
}
- 事件触发时dispatch acions.js中的函数即可
import { add,sub } from './redux/countActions';
//加
add = () =>{
const {selectValue} = this.state;
store.dispatch(add(selectValue));
}
//减
sub = () =>{
const {selectValue} = this.state;
store.dispatch(sub(selectValue));
}
//异步加
asyncAdd = () =>{
const {selectValue} = this.state;
setTimeout(() => {
store.dispatch(add(selectValue));
}, 1000);
}
//奇数加
oddAdd = () =>{
const {selectValue} = this.state;
const count = store.getState();
if(count%2===1){
store.dispatch(add(selectValue));
}
}
异步action
上面的例子有一个异步加的方法,他其实就是一个异步方法。异步action不是必须要使用的,可以把异步方法写到组件内。这样就不需要使用异步action了。就向上面的例子一样。
这个例子与异步action的区别就好比去饭店买了一份蛋炒饭,你可以等待5分钟再通知服务员点餐。这可以告诉服务员一份蛋炒饭5分钟之后上菜。这两种方法都可以实现5分钟之后来一份蛋炒饭这个目的,只是实现的方式不同,所以异步的action不是必须使用的。
同步action返回的是一个对象,异步action返回的是一个函数。
怎么使用异步action呢
redux不支持异步action,因此我们需要下载一个插件redux-thunk。可以用它通知store当接收到一个异步action返回的函数时,执行一下这个函数。
不引入这个插件会报错
- 引入插件,在store.js中
import {createStore,applyMiddleware} from 'redux';
import {countReducers} from './countReducers'
import thunk from 'redux-thunk';
export default createStore(countReducers,applyMiddleware(thunk));
- 写一个异步的action,异步action通常都要分发一个同步的action。返回的函数自带一个dispatch方法不需要特意的去引用store
import { ADD,SUB } from './constant';
export const add = (data) =>({type: ADD,data});
export const sub = (data) =>({type:SUB,data});
export const asyncAdd = (data) =>{
return (dispatch) =>{
setTimeout(() => {
dispatch(add(data)) //store.dispathch(add(data))
}, 1000);
}
}
- 把之前的异步加改写
//异步加
asyncAdd = () =>{
const {selectValue} = this.state;
store.dispatch(asyncAdd(selectValue));
}
异步action完成
react-redux的基本使用
- 所有的UI组件都应该包裹一个容器组件, 他们是父子关系
- 容器组件是真正和redux打交道的,里面可以随意的使用redux的api
- UI组件中不能使用任何redux的api
- 容器组件会传给UI组件:(1).redux中保存的状态。(2)用于操作状态的方法
- 备注:容器给UI传递:状态、操作状态的方法,均通过props传递。
- 在react-redux中引入store
import React, { Component } from 'react';
import Count from './Container/Count'
import store from './redux/store'
export default class App extends Component {
render() {
return (
<div>
<Count store={store}></Count>
</div>
);
}
}
- 创建ui组件 CountUI
import React, { Component } from 'react';
class CountUI extends Component {
state={
selectValue : 1
}
//加
add = () =>{
const {selectValue} = this.state;
this.props.add(selectValue)
}
//减
sub = () =>{
const {selectValue} = this.state;
this.props.sub(selectValue)
}
//异步加
asyncAdd = () =>{
const {selectValue} = this.state;
this.props.asyncAdd(selectValue)
}
//奇数加
oddAdd = () =>{
const {selectValue} = this.state;
if(this.props.count % 2 ===1){
this.props.add(selectValue)
}
}
render() {
return (
<div>
<h1>count:{this.props.count}</h1>
<select onChange={(e)=>this.setState({selectValue:e.target.value})}>
<option value='1'>1</option>
<option value='2'>2</option>
<option value='3'>3</option>
</select>
<button onClick={this.add}>+</button>
<button onClick={this.sub}>-</button>
<button onClick={this.asyncAdd}>异步加</button>
<button onClick={this.oddAdd}>奇数加</button>
</div>
);
}
}
export default CountUI;
- 创建容器组件 Count.jsx 通过connect和store交换数据,然后通过props的方式传给子组件CountUI
import { connect } from 'react-redux';
import CountUI from '../Components/CountUI'
import {add,asyncAdd,sub} from '../redux/countActions'
const mapStateToProps = (state) =>{
return{
count: state
}
}
const mapDispatchToProps = (dispatch) =>{
return {
add : (data)=>dispatch(add(data)),
asyncAdd : (data)=>dispatch(asyncAdd(data)),
sub : (data)=>dispatch(sub(data)),
}
}
export default connect(mapStateToProps,mapDispatchToProps)(CountUI)
优化react-redux
- connect优化 当你的mapDispatchToPros传入的是一个对象时。react-redux会帮你自动dispatch
import { connect } from 'react-redux';
import CountUI from '../Components/CountUI'
import {add,asyncAdd,sub} from '../redux/countActions'
// const mapStateToProps = (state) =>{
// return{
// count: state
// }
// }
// const mapDispatchToProps = (dispatch) =>{
// return {
// add : (data)=>dispatch(add(data)),
// asyncAdd : (data)=>dispatch(asyncAdd(data)),
// sub : (data)=>dispatch(sub(data)),
// }
// }
export default connect(
state=>({count:state}),
{
add,
asyncAdd,
sub
}
)(CountUI)
- 不需要监听redux的状态改变
import React from 'react';
import ReactDOM from 'react-dom'
import App from "./App";
ReactDOM.render(<App />,document.getElementById('root'));
// store.subscribe(()=>{
// ReactDOM.render(<App />,document.getElementById('root'));
// })
- 可以试用 来进行store的引入,而不需要一个个的传入store
app.js
import React, { Component } from 'react';
import Count from './Container/Count'
import store from './redux/store'
export default class App extends Component {
render() {
return (
<div>
<Countx></Count>
//<Count1 store={store}></Count1>
//<Count2 store={store}></Count2>
//<Count3 store={store}></Count3>
//<Count4 store={store}></Count4>
</div>
);
}
}
修改index.js
import React from 'react';
import ReactDOM from 'react-dom'
import App from "./App";
import store from './redux/store';
import {Provider} from 'react-redux'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
,document.getElementById('root'));
- 合并UI组件和容器组件,删除components。把countUI复制到容器组件count.jsx中。这样两个文件就合并成了一个文件
import { connect } from 'react-redux';
import React, { Component } from 'react';
import {add,asyncAdd,sub} from '../redux/countActions'
class CountUI extends Component {
state={
selectValue : 1
}
//加
add = () =>{
const {selectValue} = this.state;
this.props.add(selectValue)
}
//减
sub = () =>{
const {selectValue} = this.state;
this.props.sub(selectValue)
}
//异步加
asyncAdd = () =>{
const {selectValue} = this.state;
this.props.asyncAdd(selectValue)
}
//奇数加
oddAdd = () =>{
const {selectValue} = this.state;
if(this.props.count % 2 ===1){
this.props.add(selectValue)
}
}
render() {
return (
<div>
<h1>count:{this.props.count}</h1>
<select onChange={(e)=>this.setState({selectValue:e.target.value})}>
<option value='1'>1</option>
<option value='2'>2</option>
<option value='3'>3</option>
</select>
<button onClick={this.add}>+</button>
<button onClick={this.sub}>-</button>
<button onClick={this.asyncAdd}>异步加</button>
<button onClick={this.oddAdd}>奇数加</button>
</div>
);
}
}
export default connect(
state=>({count:state}),
{
add,
asyncAdd,
sub
}
)(CountUI)
react-redux最终版
新增一个组件person 和 count 共享状态
修改目录结构
redux文件夹下新建actions文件夹和reducers文件夹,把所有的action和reducer全部放入对应的文件夹中
person的action为
import { ADD_PERSON } from "../constant";
export const addPerson = (obj) => ({type: ADD_PERSON, data:obj})
person的reducer为
import {
ADD_PERSON
} from "../constant";
const initPerson = [{
name: '张三',
id: 1,
hobby: '篮球'
}]
export const person = (preState = initPerson, action) => {
const {
type,
data
} = action;
switch (type) {
case ADD_PERSON:
return [...preState, data]
default:
return preState
}
}
因为现在有person和count两个reducer所以给store就不能只是count的reducer了。这时候就要用combineReducers来把reducer联合一起
修改store.js
import {createStore,applyMiddleware,combineReducers} from 'redux';
import {count} from './reducers/count'
import { person } from './reducers/person';
import thunk from 'redux-thunk';
export default createStore(combineReducers({count,person}),applyMiddleware(thunk));
因为修改combineReducers就相当于修改了redux里面存储的结构。只有一个count组件时,redux里面仅仅是一个0。现在redux存储的是一个对象,{count:0,person:{…person内容}}
所以count.jsx的connect也应该修改
export default connect(
state=>({count:state.count,person: state.person}),
{
add,
asyncAdd,
sub
}
)(CountUI)
person.jsx组件内容
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { addPerson } from '../redux/actions/person';
class Person extends Component {
state = {}
add = () =>{
this.props.addPerson({name:this.name.value,hobby:this.hobby.value,id:this.name.value})
}
render() {
return (
<div>
<input ref={(name)=> this.name = name} type="text" />
<input ref={(hobby)=> this.hobby = hobby} type="text" />
<button onClick={this.add}>添加</button>
<h1>和为:{this.props.count}</h1>
<ul>
{
this.props.person.map((p)=>{
return <li key={p.id}>{p.name}-----{p.hobby}</li>
})
}
</ul>
</div>
);
}
}
export default connect(
state => ({ count: state.count, person: state.person }),
{
addPerson
})(Person);