redux

redux工作流
在这里插入图片描述React UI库 框架
想要状态管理机制,使用Redux
如果想要异步交互,使用Redux-thunk或者Redux-saga中的一个
如果想要简化代码,获取业务逻辑与UI组件分离,那么使用react-redux
redux只能做同步操作,如果在redux中使用异步,需要借助中间件(redux-thunk,redux-saga)。

redux-saga学习成本较高,需要首先了解ES6的Generator函数

react-redux类组件->函数组件
	用户写的组件-->函数组件-->无状态组件-->没有state
	从效率来说,函数组件的运行效率比函数组件要高。
	UI组件和容器组件
	用户写的组件UI组件
	使用react-redux的connect方法,将一个UI组件内部包裹一个容器组件,UI组件如果想要获取数据,props去获取。
	直接将数据仓库绑定到更组件上,内部的子组件都可以使用。

1、安装redux
yarn add redux 或 yarn add react-redux
2、创建reducer
根目录下创建store文件夹,文件夹下创建reducer.js

//函数reducer
const initState = {
	inputValue:'input的数据',
	listData:[{
		text:'备忘录1',
		time:'2021-01-04',
		status:'未完成'
	}]
}
export default (state = initState,action)=>{
	console.log('reducer');
	//action就是组建中分发的那个action
	if(action.type == 'TO_CHANGE_INPUT'){
		return {
			...state,
			//给获取到的数据命名存入store
			inputValue:action.value
		}
	}
	if(action.type == 'TO_ADD'){
		//修改仓库
		let obj = {
			text:state.inputValue,
			time:'',
			status:'未完成'
		}
		let {listData} = state;
		listData.push(obj);
		return {
			...state,
			listData
		}
	}
	return{
		...state
	}
}

3、创建store
store下新建文件 index.js

//仓库 store
import {creatStore} from 'redux';
import reducer from './reducer';
//将reducer与store绑定,创建stroe并返回
export default creatStore(reducer);

4、使用
在constructor里 this.state = store.getState();将store中的数据导入到当前组件的state

订阅者模式
store中的数据更改,页面的订阅者发现,去修改页面的数据,让页面重新渲染
store.subscribe(this.storeChange)//订阅Redux的状态,如果redux中状态发生更改,需要监听并渲染

import store from '../store';
class ToDo extends Components{
	constructor(props){
		super(props);
		this.state = store.getState();
		store.subscribe(thihs.stateChange);//订阅者
	}
	stateChange = ()=>{
		this.setState({
			...store.getState()
		});
	}
	inputChange=(e)=>{
		//获取input数据,更改store中的数据
		let action = {
			type:'TO_CHANGE_INPUT',
			value:e.target.value
		};
		//分发action
		stroe.dispatch(action);
	}
	//添加
	toAdd=(e)=>{
		let action ={
			type:'TO_ADD',
		}
		store.dispatch(action);
	}
	render(){
		return (
			<div style={{padding:20px}}>
				<h2>备忘录</h2>
				<div>
					<Input tyoe="text" value={this.state.inputValue} onChange={this.inputCHange}/>
					<Button type="primary" onClick={this.toAdd}>添加</Button>
				</div>
				<div>
					{this.state.listData.map((item,index)=>{
						<div key={index} style={{color:item.status === '未完成'?'red':'green'}}>
							{item.text}$nbsp$nbsp{item.time}$nbsp$nbsp{item.status}
							<span>{
								item.status === '未完成' && <Button type="link" onClick={this.toChangeStatus.bind(this,index)}>完成</Button>
							}</span>
						</div>
					})}
				</div>
			</div>
		)
	}
}

5、大型项目代码的拆分

创建项目,yarn add redux;

src之下新建文件夹pages,
pages之下新建test1.js和test2.js, //用来写组件
src之下新建文件夹store,
store中新建index.js //产生store
store之下新建文件夹actionCtreators,
actionCtreators之下新建文件test1Action.js和test2Action.js
store之下新建文件夹reducers,
reducers之下新建文件test1Reducer.js和test2Reducer.js,index.js
store之下新建文件actionType.js

//test1组件
//展示store的数据
import React,{Component} from 'react';
import {changeTest1A} from '../store/actionCreators/test1Action'
import {changeTest2A} from '../store/actionCreators/test2Action'
class Test1 extends Component{
	constructor(props){
		super(props);
		this.state = store.getState();
		store.subscribe(this.changeState);//订阅
		//若是此处只想要test1中的数据this.state = store.getState().tets1;
	}
	changeTest1 = ()=>{
		//分发action,action对象
		store.dispatch(changeTest1A('123'));
	}
	changeTest2 = ()=>{
		//更该test的基本数据
		store.dispatch(changeTest2('2323'));
	}
	changeState = ()=>{
		this.setState(store.getState())
	}
	render(){
		return(
			<div>
				{JSON.stringify(this.state)}
				<button onClick={this.changeTest1}>更改test1的数据</button>
				<button onClick={this.changeTest2}>更改test2的数据</button>
			</div>
		)
	}
}
export default Test1;
//Test1Action创造器
import {CHANGE_TEST1} from '../actionTypes'
export const chanTest1A = (value)=>{
	//返回的是action对象
	retun {
		type:'CHANGE_TEST1',//必不可少,代表action的类型
		value
	}
}
//Test1reducer,业务逻辑处理
import {CHANGE_TEST1} from '../actiontypes';
let test1State = {
	msg:'test1'
};
export default (state = test1State,action)=>{
	//业务逻辑处理
	if(action.type === CHANGE_TEST1){
		return {
			...state,
			msg:action.value
		}
	}
	return state;
}
// Test2组件
// 使用React-Redux去做
// yarn add react-redux
import React from 'react';
import { connect } from 'react-redux';
import { changeTest2A, toGetDataA } from '../store/actionCreators/test2Action';
const Test2 = (props) => {
  return (
    <div>
      <h3>react-redux</h3>
      {JSON.stringify(props)}
      <button onClick={props.changeTest2}>修改test2的数据</button>
      <br />
      <button onClick={props.toGetData}>获取数据</button>
      {JSON.stringify(props.arr)}
    </div>
  );
}
// connect(mapStateToProps)(Test2)
// connect的第一个参数mapStateToProps是一个函数,函数有一个参数state,代表store中的所有的数据。返回的对象,就是UI组件的props的数据
const mapStateToProps = (state, ownProps) => {
  return {
    test: state.test2.msg,
    value: 'hello',
    arr: state.test2.arr
  }
}
const mapDispatchToProps = (dispatch, ownProps) => {
  return {
    changeTest2: () => {
      // 分发action
      dispatch(changeTest2A('0000'))
    },
    toGetData: () => {
      // 分发action
      dispatch(toGetDataA())
    }
  }
}
export default connect(mapStateToProps, mapDispatchToProps)(Test2);
// export default connect(state => state.test2)(Test2); 如果store中只需要test2的数据
// export default connect(state => state)(Test2);
// Test2Action创建器
import { CHANGE_TEST2, TO_GET_DATA } from '../actionTypes'
export const changeTest2A = (value) => {
  return {
    type: CHANGE_TEST2,
    value
  };
}
export const toGetDataA = () => {
  return {
    type: TO_GET_DATA
  };
}
// Test2reducer,业务逻辑处理
import { CHANGE_TEST2, TO_GET_DATA } from '../actionTypes'
import axios from 'axios';
let test2State = {
  msg: 'test2',
  arr: []
};
export default (state = test2State, action) => {
  if (action.type === CHANGE_TEST2) {
    return {
      ...state,
      msg: action.value
    };
  }
  // reducer是一个纯函数,它的返回值不能有除参数以外的决定,如果写了异步操作,需要用中间件
  if (action.type === TO_GET_DATA) {
    // 获取后台数据,更改store数据,然后再返回
    axios.get('http://134.175.154.93:8099/manager/category/findAllCategory').then((res) => {
      let arr = res.data.data;
      return {
        ...state,
        arr
      }
    }).catch((error) => {
      console.log(error);
    })
  }
  return state;
}
//action的type的类型
export const CHANGE_TEST1 = 'CHANGE_TEST1';
export const CHANGE_TEST2 = 'CHANGE_TEST2';
export const TO_GET_DATA = 'TO_GET_DATA';
//reducers文件夹下的index.js,整合所有的reducer,导出一个合并之后reducer
import {combineReducers} from 'redux';
import test1Reducer from './test1Reducer';
import test2Reducer from './test2Reducer';
const reducer = combineReducers({
	//需要整合的reducer
	// 大仓库中有两个初始化数据,写的什么属性,初始化数据放到什么属性上
	test1:test1Reducer,
	test2:test2Reducer
})
export default reducer;
//store文件夹下的index.js
import {createStore} from 'redux';
import reducer from './reducers'
// 关于谷歌插件(REDUX_DEVTOOLS)的配置
const store = createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
export default store;

react-redux
将所有组件分成两大类:
UI组件(负责UI的呈现)和容器组件(负责管理数据和逻辑)
UI组件
1、只负责UI的呈现,不带有任何业务逻辑
2、没有状态,即不适用this.state这个变量
3、所有数据都有参数(this.props)提供
4、不适用任何Redux的API
5、因为不含有状态,UI组件又称为“纯组件”,即他跟纯函数一样,纯粹由参数决定它的值。
容器组件
1、负责管理数据和业务逻辑,不负责UI的呈现。
2、带有内部状态
3、使用redux的API
4、容器组件包裹UI组件

UI组件和容器组件的结合
1、如果一个组件既有UI又有业务逻辑,那么将他拆分成两层结构:外面是一个容器组件,里面包了一个UI组件。前者负责与外部的通信,将数据传给后者,由后者渲染出视图。
2、React-Redux规定,所有的UI组件都由用户提供,容器组件则是由React-Redux自动生成。

React-Redux中的connect()
用于从UI组件生成容器组件
React-Redux中的mapStateToProps()
1、mapStateToProps是一个函数。他的作用是建立一个从外部state对象到UI组件的props对象的映射关系。执行后返回一个对象,里面的每一个键值对就是一个映射(UI组件可以访问的数据)。
2、mapStateToProps会订阅(绑定)store,每当state更新的时候,就会从自动执行,重新计算UI组件的参数,从而出发UI组件的重新渲染。
3、mapStateToProps的第一个参数总是state对象,还可以使用第二个参数,代表容器组件的props对象。使用ownProps作为参数后,如果容器组件的参数发生变化,也会引发UI组件重新渲染。
4、如果connect方法省略mapStateToProps参数,那么UI组件就不会订阅Store,就是说Store的更新不会引起UI组件的更新。

connect(mapStateToProps,mapDispatchToProps)(UICom)

React-Redux中的mapDispatchToProps()
1、mapDispatchToProps是connect函数的第二个参数,用来建立UI组件的参数到store.dispatch方法的映射。也就是说,他定义了用户的那些操作应该当做Action,传给Store。他可以是一个函数,也可以使一个对象。
2、如果mapDispatchToProps是一个函数,会得到dispatch和ownProps(容器组件的props对象)两个参数,应该返回一个对象,该对象的每个键值对都是一个映射,定义了 UI 组件的参数怎样发出 Action。
3.如果mapDispatchToProps是一个对象,它的每个键名也是对应 UI 组件的同名参数,键值应该是一个函数,会被当作 Action creator ,返回的 Action 会由 Redux 自动发出。

React-Redux的<Provider> 组件
connect方法生成容器组件以后,需要让容器组件拿到state对象,才能生成 UI 组件的参数。一种解决方法是将state对象作为参数,传入容器组件。但是容器组件可能在很深的层级,一级级将state传下去就很麻烦。React-Redux 提供Provider组件,可以让容器组件拿到state。在根组件外面包了一层Provider,App的所有子组件都可以拿到state了。它的原理是React组件的context属性,store放在了上下文对象context上面。React-Redux自动生成的容器组件的代码,就类似下面这样,然后子组件就可以从context拿到store。

安装
yarn add react-redux
使用
1.在项目index.js里,引入Provider,引入store,然后使用

    import { Provider } from 'react-redux'
    import store from './store'
  <Provider store={store}><App /></Provider>

<Provider>是一个提供器,只要使用了这个组件,组件里边的其它所有组件都可以使用store了
2.使用store中的数据
在需要使用store数据的地方,引入连接器
import {connect} from ‘react-redux’ //引入连接器
创建映射关系,把原来的state映射成组件中的props属性

  const stateToProps = (state)=>{
      return {
          inputValue : state.inputValue
      }
  }
导出组件
  export default connect(mapStateToProps,mapDispatchToProps)(TodoList)

  mapStateToProps传入所有state,返回指定的state数据
    function mapStateToProps(state) {
        return { todos: state.todos }
      }
  mapDispatchToProps,传入dispatch,返回的对象中的方法会在组件的props中
  const mapDispatchToProps = (dispatch) => {
    return {
      inputChange(e) {
        dispatch(changeInputAction(e.target.value));
      }
    }
  }
  connect的作用是把UI组件(无状态组件)和业务逻辑代码的分开,然后通过connect再连接到一起,让代码更清晰和易于维护。这也是React-Redux最大的优点。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值