React-Redux相关知识(什么是react-redux、react-redux的原理、react-redux相关API、react-redux的基本使用)(十二)

系列文章目录

第一章:React基础知识(React基本使用、JSX语法、React模块化与组件化)(一)
第二章:React基础知识(组件实例三大核心属性state、props、refs)(二)
第三章:React基础知识(事件处理、受控组件与非受控组件、高阶函数、组件的生命周期)(三)
第四章:React脚手架应用(创建脚手架、代理配置、ajax相关、组件通信)(四)
第五章:react-router5路由相关一(路由相关概念、基本使用、NavLink与NavLink的封装、Switch的使用、严格匹配、路由重定向、路由组件与一般组件的区别)(五)
第六章:react-router5路由相关二(嵌套路由、路由传参、replace、编程式路由导航、withRouter的使用、BrowserRouter与HashRouter的区别)(六)
第七章:React-Router6路由相关一(路由的基本使用、重定向、NavLink·、路由表、嵌套路由)(七)
第八章:React-Router6路由相关二(路由传参、编程式路由导航、路由相关hooks)(八)
第九章:React相关扩展一(setState、lazyLoad、Hooks相关)(九)
第十章:React相关扩展二(Fragment、Content、组件优化、render props、错误边界)(十)
第十一章:Redux相关知识(什么是redux、redux的工作原理、redux的核心概念、redux的基本使用)(十一)
第十二章:React-Redux相关知识(什么是react-redux、react-redux的原理、react-redux相关API、react-redux的基本使用)(十二)



如果还不会redux建议看完第十一章后再来看这章,这样会更好理解

一、什么是react-redux

1.1 概念

  • Ract-ReduxRedux的官方React绑定库,它能够使你的React组件从Redux store中读取数据,并且向store分发actions以更新数据。
  • 用redux 库来创建 store , 利用 react-redux 库来获取 store 中的数据或者更新数据。
  • react-redux提供了两个常用的 api ,一个是:Provider,一个是:connect。组件之间共享的数据是Provider这个顶层组件通过props传递下去的,store必须作为参数放到Provider组件中去。而connect则提供了组件获取 store中数据或者更新数据的接口。
  • 学习网站: https://react-redux.js.org/

1.2 react-redux的原理

在这里插入图片描述
React-Redux 将所有组件分成两大类: UI 组件(负责 UI 的呈现)和容器组件(负责管理数据和逻辑)。

  • UI组件

    • 只负责 UI 的呈现,不带有任何业务逻辑
    • 没有状态(即不使用this.state这个变量)
    • 所有数据都由参数(this.props)提供
    • 不使用任何 Redux 的 API
    • 因为不含有状态,UI 组件又称为"纯组件",即它跟纯函数一样,纯粹由参数决定它的值。
  • 容器组件

    • 负责管理数据和业务逻辑,不负责 UI 的呈现
    • 带有内部状态
    • 使用 Redux 的 API
  • UI 组件和容器组件的结合

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

二、相关API

react-redux在7.1版本之前使用connect函数配合Provider来进行操作。以下介绍7.1版本之前的Provider及connect函数。

2.1 <Provider>组件

概念:connect方法生成容器组件以后,需要让容器组件拿到state对象,才能生成 UI 组件的参数。 React-Redux 提供Provider组件,可以让容器组件拿到state。
用法:在根组件外面包了一层Provider,App的所有子组件都可以拿到state了。它的原理是React组件的context属性,store放在了上下文对象context上面。React-Redux自动生成的容器组件的代码,就类似下面这样,然后子组件就可以从context拿到store。
示例代码

import store from './redux/store'

// src/index.js
<Provider store={store}>
 <App />
</Provider>

2.2 connect()

React-Redux中的connect()方法用于从 UI 组件生成容器组件,connect(mapStateToProps?,
mapDispatchToProps?, mergeProps?, options?) 。之后就可以在该组件的props中获取到仓库中数据和dispatch方法。

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

2.3 mapStateToProps()

mapStateToProps 字面含义是把state映射到props中去,意思就是把Redux中的数据映射到React中的props中去。

  1. mapStateToProps是一个函数。它的作用是建立一个从外部state对象到 UI 组件的props对象的映射关系。执行后返回一个对象,里面的每一个键值对就是一个映射。
  2. mapStateToProps会订阅(绑定) Store,每当state更新的时候,就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。
  3. mapStateToProps的第一个参数总是state对象,还可以使用第二个参数,代表容器组件的props对象。使用ownProps作为参数后,如果容器组件的参数发生变化,也会引发 UI 组件重新渲染。
  4. 如果connect方法省略mapStateToProps参数,那么UI 组件就不会订阅Store,就是说 Store 的更新不会引起 UI 组件的更新。
// 映射仓库中的数据到props中
const mapStateToProps = (state, ownProps) => {
 return {
	 inputValue: state.inputValue,
	 test: 'hello',
	 listData: state.listData
 }
}

2.4 mapDispatchToProps()

mapStateToProps 字面含义是把dispatch映射到props中去,意思就是把Redux中的修改数据的方法映射到React中的props中去。

  1. mapDispatchToProps是connect函数的第二个参数,用来建立 UI 组件的参数到store.dispatch方法的映射。也就是说,它定义了用户的哪些操作应该当作 Action,传给 Store。它可以是一个函数,也可以是一个对象。
  2. 如果mapDispatchToProps是一个函数,会得到dispatch和ownProps(容器组件的props对象)两个参数,应该返回一个对象,该对象的每个键值对都是一个映射,定义了 UI 组件的参数怎样发出 Action。
  3. 如果mapDispatchToProps是一个对象,它的每个键名也是对应 UI 组件的同名参数,键值应该是一个函数,会被当作 Action creator ,返回的 Action 会由 Redux 自动发出。
// 映射仓库中的dispatch到props中
const mapDispatchToProps = (dispatch, ownProps) => {
 return {
	 inputChange: (e) => {
	 // 获取input数据,更改store中的数据
		 let action = {
			 type: 'TO_CHANGE_INPUT',
			 value: e.target.value
		 };
		 // 分发action
		 dispatch(action);
	 },
	 // 添加
	 toAdd: () => {
	 	let action = {
		 type: 'TO_ADD'
		 };
		 dispatch(action);
	 },
	 // 删除
	 toDelete: (index) => {
		 // console.log(index);
		 let action = {
			 type: 'TO_DELETE',
			 index
		 };
		 dispatch(action);
	 },
	 // 完成
	 toChangeStatus: (index) => {
		 let action = {
			 type: 'TO_CHANGE_STATUS',
			 index
		 };
		 dispatch(action);
	 },
 }
}

三、react-redux的基本使用

  • 安装

     npm install react-redux --save
    # 或
     yarn add react-redux
    
  • 代码案例片段:

    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')
    )
    

    App.js

    import React, { Component } from 'react'
    import Count from './containers/Count' //引入的Count的容器组件
    import Person from './containers/Person' //引入的Person的容器组件
    
    export default class App extends Component {
    	render() {
    		return (
    			<div>
    				<Count/>
    				<hr/>
    				<Person/>
    			</div>
    		)
    	}
    }
    
    

    redux/action/count.js(该文件专门为Count组件生成action对象)

    //同步action,就是指action的值为Object类型的一般对象
    export const createIncrementAction = data => ({type:'increment',data})
    export const createDecrementAction = data => ({type:'decrement',data})
    
    //异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的。
    export const createIncrementAsyncAction = (data,time) => {
    	return (dispatch)=>{
    		setTimeout(()=>{
    			dispatch(createIncrementAction(data))
    		},time)
    	}
    }
    

    redux/action/person.js(该文件专门为Person组件生成action对象)

    //创建增加一个人的action动作对象
    export const addPerson = personObj => ({type:'add_person',data:personObj})
    

    redux/reducer/count.js

    /* 
    	1.该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
    	2.reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
    */	
    const initState = {
    	value:0,index:1
    } 
    //初始化状态
    export default function countReducer (preState = initState, action) {
    	console.log('preState:',preState);
    	console.log('action:',action);
    
    	//从action对象中获取:type、data
    	const { type, data } = action
    	//根据type决定如何加工数据
    	switch (type) {
    		case 'increment': //如果是加
    			return {
    				...preState,
    				value: preState.value + data
    			}
    		case 'decrement': //若果是减
    			return {
    				...preState,
    				value: preState.value - data
    			}
    		default:
    			return preState
    	}
    }
    

    redux/reducer/person.js

    //初始化人的列表
    const initState = [{id:'001',name:'tom',age:18}]
    
    export default function personReducer(preState=initState,action){
    	// console.log('personReducer@#@#@#');
    	const {type,data} = action
    	switch (type) {
    		case 'add_person': //若是添加一个人
    			//preState.unshift(data) //此处不可以这样写,这样会导致preState被改写了,personReducer就不是纯函数了。
    			return [data,...preState]
    		default:
    			return preState
    	}
    }
    

    redux/reducer/index.js

    /* 
    	该文件用于汇总所有的reducer为一个总的reducer
    */
    //引入combineReducers,用于汇总多个reducer
    import {combineReducers} from 'redux'
    //引入为Count组件服务的reducer
    import count from './count'
    //引入为Person组件服务的reducer
    import persons from './person'
    
    //汇总所有的reducer变为一个总的reducer
    export default  combineReducers({
    	count,
    	persons
    })
    
    

    redux/store.js

    /* 
    	该文件专门用于暴露一个store对象,整个应用只有一个store对象
    */
    
    //引入createStore,专门用于创建redux中最为核心的store对象
    // 旧版
    // import { createStore } from 'redux'
    //新版
    import { legacy_createStore as createStore, applyMiddleware } from 'redux'
    //引入汇总之后的reducer
    import reducer from './reducers'
    //引入redux-thunk,用于支持异步action
    import thunk from 'redux-thunk'
    //引入redux-devtools-extension
    import {composeWithDevTools} from 'redux-devtools-extension'
    
    //暴露store 
    export default createStore(reducer,composeWithDevTools(applyMiddleware(thunk)))
    

    container/Count.jsx

    import React, { Component } from "react";
    //引入action
    import {
      increment,
      decrement,
      incrementAsync,
    } from "../../redux/actions/count";
    //引入connect用于连接UI组件与redux
    import { connect } from "react-redux";
    
    //定义UI组件
    class Count extends Component {
      state = { carName: "奔驰c63" };
    
      //加法
      increment = () => {
        const { value } = this.selectNumber;
        this.props.increment(value * 1);
      };
      //减法
      decrement = () => {
        const { value } = this.selectNumber;
        this.props.decrement(value * 1);
      };
      //奇数再加
      incrementIfOdd = () => {
        const { value } = this.selectNumber;
        if (this.props.count % 2 !== 0) {
          this.props.increment(value * 1);
        }
      };
      //异步加
      incrementAsync = () => {
        const { value } = this.selectNumber;
        this.props.incrementAsync(value * 1, 500);
      };
    
      render() {
        //console.log('UI组件接收到的props是',this.props);
        return (
          <div>
            <h2>我是Count组件,下方组件总人数为:{this.props.renshu}</h2>
            <h4>当前求和为:{this.props.count}</h4>
            <select ref={(c) => (this.selectNumber = c)}>
              <option value="1">1</option>
              <option value="2">2</option>
              <option value="3">3</option>
            </select>
            &nbsp;
            <button onClick={this.increment}>+</button>&nbsp;
            <button onClick={this.decrement}>-</button>&nbsp;
            <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;
            <button onClick={this.incrementAsync}>异步加</button>&nbsp;
          </div>
        );
      }
    }
    
    
    //使用connect()()创建并暴露一个Count的容器组件
    //写法一
    /*
     
    	//1.mapStateToProps函数返回的是一个对象;
    	//2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value
    	//3.mapStateToProps用于传递状态
    
    function mapStateToProps(state){
    	return {count:state.count.value}
    }
    
    
    	//1.mapDispatchToProps函数返回的是一个对象;
    	//2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value
    	//3.mapDispatchToProps用于传递操作状态的方法
    
    function mapDispatchToProps(dispatch){
    	return {
    		jia:number => dispatch(createIncrementAction(number)),
    		jian:number => dispatch(createDecrementAction(number)),
    		jiaAsync:(number,time) => dispatch(createIncrementAsyncAction(number,time)),
    	}
    }
    
    //使用connect()()创建并暴露一个Count的容器组件
    export default connect(mapStateToProps,mapDispatchToProps)(Count)
    */
    
    //写法二
    /*
    export default connect(
    	state => ({count:state.count.value}),
    
    	//mapDispatchToProps的一般写法
    	//dispatch => ({
    	//	jia:number => dispatch(createIncrementAction(number)),
    	//	jian:number => dispatch(createDecrementAction(number)),
    	//	jiaAsync:(number,time) => dispatch(createIncrementAsyncAction(number,time)),
    	// }) 
    
    	//mapDispatchToProps的简写
    	{
    		jia:createIncrementAction,
    		jian:createDecrementAction,
    		jiaAsync:createIncrementAsyncAction,
    	}
    )(Count)
    
    */
    
    
    //写法三(推荐)
    //使用connect()()创建并暴露一个Count的容器组件
    export default connect(
      (state) => ({
        count: state.count.value,
        personCount: state.persons.length,
      }),
      { increment, decrement, incrementAsync }
    )(Count);
    

    container/Person.jsx

    import React, { Component } from 'react'
    import {nanoid} from 'nanoid'
    import {connect} from 'react-redux'
    import {addPerson} from '../../redux/actions/person'
    
    class Person extends Component {
    
    	addPerson = ()=>{
    		const name = this.nameNode.value
    		const age = this.ageNode.value*1
    		const personObj = {id:nanoid(),name,age}
    		this.props.addPerson(personObj)
    		this.nameNode.value = ''
    		this.ageNode.value = ''
    	}
    
    	render() {
    		return (
    			<div>
    				<h2>我是Person组件,上方组件求和为{this.props.count}</h2>
    				<input ref={c=>this.nameNode = c} type="text" placeholder="输入名字"/>
    				<input ref={c=>this.ageNode = c} type="text" placeholder="输入年龄"/>
    				<button onClick={this.addPerson}>添加</button>
    				<ul>
    					{
    						this.props.persons.map((p)=>{
    							return <li key={p.id}>{p.name}--{p.age}</li>
    						})
    					}
    				</ul>
    			</div>
    		)
    	}
    }
    
    export default connect(
    	state => ({
    		persons:state.persons,
    		count:state.count.value
    	}),//映射状态
    	{addPerson}//映射操作状态的方法
    )(Person)
    

    运行结果:
    在这里插入图片描述
    持续更新~~~

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值