react复习笔记

js创建虚拟dom和jsx创建虚拟dom

  • js创建虚拟dom

     <!-- 准备好一个“容器” -->
     <div id="test"></div>
    
     <!-- 引入react核心库 -->
     <script type="text/javascript" src="../js/react.development.js"></script>
     <!-- 引入react-dom,用于支持react操作DOM -->
     <script type="text/javascript" src="../js/react-dom.development.js"></script>
    
     <script type="text/javascript" > 
     	//1.创建虚拟DOM
     	const VDOM = React.createElement('h1',{id:'title'},React.createElement('span',{},'Hello,React'))
     	//2.渲染虚拟DOM到页面
     	ReactDOM.render(VDOM,document.getElementById('test'))
     </script>
    

js创建虚拟dom,如果有嵌套结构比较繁琐!

  • jsx创建虚拟dom

     <!-- 准备好一个“容器” -->
     <div id="test"></div>
    
     <!-- 引入react核心库 -->
     <script type="text/javascript" src="../js/react.development.js"></script>
     <!-- 引入react-dom,用于支持react操作DOM -->
     <script type="text/javascript" src="../js/react-dom.development.js"></script>
     <!-- 引入babel,用于将jsx转为js -->
     <script type="text/javascript" src="../js/babel.min.js"></script>
    
     <script type="text/babel" > /* 此处一定要写babel */
     	//1.创建虚拟DOM
     	const VDOM = (  /* 此处一定不要写引号,因为不是字符串 */
     		<h1 id="title">
     			<span>Hello,React</span>
     		</h1>
     	)
     	//2.渲染虚拟DOM到页面
     	ReactDOM.render(VDOM,document.getElementById('test'))
     </script>
    

jsx最后会被babel编译成js代码的

  • 虚拟dom和真实dom的比较

     <script type="text/babel" > /* 此处一定要写babel */
     	//1.创建虚拟DOM
     	const VDOM = (  /* 此处一定不要写引号,因为不是字符串 */
     		<h1 id="title">
     			<span>Hello,React</span>
     		</h1>
     	)
     	//2.渲染虚拟DOM到页面
     	ReactDOM.render(VDOM,document.getElementById('test'))
    
     	const TDOM = document.getElementById('demo')
     	console.log('虚拟DOM',VDOM);
     	console.log('真实DOM',TDOM);
     	debugger;
     	// console.log(typeof VDOM);
     	// console.log(VDOM instanceof Object);
     	/* 
     			关于虚拟DOM:
     				1.本质是Object类型的对象(一般对象)
     				2.虚拟DOM比较“轻”,真实DOM比较“重”,因为虚拟DOM是React内部在用,无需真实DOM上那么多的属性。
     				3.虚拟DOM最终会被React转化为真实DOM,呈现在页面上。
     	 */
     </script>
    

虚拟dom上的属性比较少

关于jsx

  • jsx中标签代码中,如果要写js代码,要加上{},而且{}里面只能写一个表达式

    • {}中的语句可以给变量赋值就是表达式,如arr.map(), function test () {}
  • jsx标签代码中,如果写数组,会直接被遍历

  • 更多

/* 
				jsx语法规则:
						1.定义虚拟DOM时,不要写引号。
						2.标签中混入JS表达式时要用{}。
						3.样式的类名指定不要用class,要用className。
						4.内联样式,要用style={{key:value}}的形式去写。
						5.只有一个根标签
						6.标签必须闭合
						7.标签首字母
								(1).若小写字母开头,则将该标签转为html中同名元素,若html中无该标签对应的同名元素,则报错。
								(2).若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错。

		 */

函数式组件和类式组件

  • 函数式组件

    • 函数式组件,因为是window调用的,而babel中开启了严格模式,所以这里的this是undefined!

    • <script type="text/babel">
      		//1.创建函数式组件
      		function MyComponent(){
      			console.log(this); //此处的this是undefined,因为babel编译后开启了严格模式
      			return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
      		}
      		//2.渲染组件到页面
      		ReactDOM.render(<MyComponent/>,document.getElementById('test'))
      		/* 
      			执行了ReactDOM.render(<MyComponent/>.......之后,发生了什么?
      					1.React解析组件标签,找到了MyComponent组件。
      					2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。
      		*/
      	</script>
      
  • 类式组件

    • 是一个继承自React.Component的类
    • 要重写父类的render方法
    • 其state和props在实例中
    • 方法中的this是由调用者决定的,render中的this指向该实例
    • render方法会在初始化的时候调用,和更新后调用
    • 通过this.setState({})可以更新页面上的组件,setState()方法是上级类上的

组件中的三大属性

类式组件

state

  • 初始化state

    • 在构造函数中初始化state
      • this.state = {isHot:false,wind:‘微风’}
  • 获取state中的值

    • 在render中的this指向是指向该实例的
    • 如果onClick事件中的回调函数里是原型对象的方法,并在里通过this.state获取状态,就会因this指向而报错,因为这个方法不是组件对象调用的,而是window,所以this指向的是undefined
  • 对state进行操作

    • 不能直接进行操作!
    • 要用this.setState()操作,而且this.setState()是会合并之前的state中的属性的,而不是进行的替换操作
  • 解决this指向问题

    • 在构造函数中 this.changeWeather = this.changeWeather.bind(this),会在当前实例上添加类中改变this指向后的函数

    • 给实例添加箭头函数方法

      • 	<script type="text/babel">
        		//1.创建组件
        		class Weather extends React.Component{
        			//初始化状态
                    //实例上的状态属性
        			state = {isHot:false,wind:'微风'}
        
        			render(){
        				const {isHot,wind} = this.state
        				return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'}{wind}</h1>
        			}
        
        			//自定义方法————要用赋值语句的形式+箭头函数
        			changeWeather = ()=>{
        				const isHot = this.state.isHot
        				this.setState({isHot:!isHot})
        			}
        		}
        		//2.渲染组件到页面
        		ReactDOM.render(<Weather/>,document.getElementById('test'))
        				
        	</script>
        

props

  • 传递props属性,ReactDOM.render(<Person {…p}/>,document.getElementById(‘test3’))

  • 获取props,this.props

  • 对props进行限制

    • 对组件的props的进行限制和设置默认Props属性,只能写在该实例的原型对象中

      • 	    //第一中写法
                //对标签属性进行类型、必要性的限制
              		Person.propTypes = {
              			name:PropTypes.string.isRequired, //限制name必传,且为字符串
              			sex:PropTypes.string,//限制sex为字符串
              			age:PropTypes.number,//限制age为数值
              			speak:PropTypes.func,//限制speak为函数
              		}
              		//指定默认标签属性值
              		Person.defaultProps = {
              			sex:'男',//sex默认值为男
              			age:18 //age默认值为18
              		}
              		//第二中写法
              		//加上static就是给原型对象上添加属性和方法
              			//对标签属性进行类型、必要性的限制
              			static propTypes = {
              				name:PropTypes.string.isRequired, //限制name必传,且为字符串
              				sex:PropTypes.string,//限制sex为字符串
              				age:PropTypes.number,//限制age为数值
              			}
              
              			//指定默认标签属性值
              			static defaultProps = {
              				sex:'男',//sex默认值为男
              				age:18 //age默认值为18
              			}
        

ref(dom节点对象)

  • 字符串形式的ref

    • 设置ref:ref=“input2”
    • 获取ref:this.refs
    • 不常用
  • 回调函数设置ref

    • //第一种方式,会被重复调用
      //设置ref
      <input ref={c => this.input1 = c } type="text" placeholder="点击按钮提示数据"/>&nbsp;
      //第二种方式,不会被重复调用
      /*
      	saveInput = (c)=>{
      				this.input1 = c;
      				console.log('@',c);
      			}
      */
      //<input ref={this.saveInput} type="text"/><br/><br/>
      
      
  • createRef设置ref

    • //<input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>&nbsp;
      //myRef = React.createRef()
      /*
      showData = ()=>{
      				alert(this.myRef.current.value);
      			}
      */
      

函数组件

函数组件只有props属性,但是可以使用hooks添加state等属性…

限制props和设置默认的props

//创建组件
		function Person (props){
			const {name,age,sex} = props
			return (
					<ul>
						<li>姓名:{name}</li>
						<li>性别:{sex}</li>
						<li>年龄:{age}</li>
					</ul>
				)
		}
		Person.propTypes = {
			name:PropTypes.string.isRequired, //限制name必传,且为字符串
			sex:PropTypes.string,//限制sex为字符串
			age:PropTypes.number,//限制age为数值
		}

		//指定默认标签属性值
		Person.defaultProps = {
			sex:'男',//sex默认值为男
			age:18 //age默认值为18
		}

react中的事件处理

/* 
				(1).通过onXxx属性指定事件处理函数(注意大小写)
						a.React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 —————— 为了更好的兼容性
						b.React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ————————为了的高效
				(2).通过event.target得到发生事件的DOM元素对象 ——————————不要过度使用ref
			 */

高阶函数和函数的柯里化

//#region 
				/* 
					高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。
									1.若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。
									2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。
									常见的高阶函数有:Promise、setTimeout、arr.map()等等

					函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。 
						function sum(a){
							return(b)=>{
								return (c)=>{
									return a+b+c
								}
							}
						}
					*/
		//#endregion

收集表单数据

非受控组件,现用现取(使用ref获取input的值)

受控组件(将Input值放入state中)

实现方式:

使用函数的柯里化

//<input onChange={this.saveFormData('username')} type="text" name="username"/>
/*
//保存表单数据到状态中
			saveFormData = (dataType)=>{
				return (event)=>{
					this.setState({[dataType]:event.target.value})
				}
			}
*/

不使用函数的柯里化实现

//<input onChange={event => this.saveFormData('username',event) } type="text" name="username"/>
/*
	//保存表单数据到状态中
			saveFormData = (dataType,event)=>{
				this.setState({[dataType]:event.target.value})
			}
*/

关于Diff算法

如果给标签加上key就会使用到diff算法

diff算法会将新的虚拟dom和旧虚拟dom进行比较生成真实的dom

	1. 虚拟DOM中key的作用:
					1). 简单的说: key是虚拟DOM对象的标识, 在更新显示时key起着极其重要的作用。

					2). 详细的说: 当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM, 
												随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:

									a. 旧虚拟DOM中找到了与新虚拟DOM相同的key:
												(1).若虚拟DOM中内容没变, 直接使用之前的真实DOM
												(2).若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM

									b. 旧虚拟DOM中未找到与新虚拟DOM相同的key
												根据数据创建新的真实DOM,随后渲染到到页面
									
			2. 用index作为key可能会引发的问题:
								1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
												会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。

								2. 如果结构中还包含输入类的DOM:
												会产生错误DOM更新 ==> 界面有问题。
												
								3. 注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,
									仅用于渲染列表用于展示,使用index作为key是没有问题的。
					
			3. 开发中如何选择key?:
								1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
								2.如果确定只是简单的展示数据,用index也是可以的。

Es6扩展

扩展运算符…

扩展运算符是不能展开对象的属性的,但是可以用{…xx}生成新的对象,[…arr]也是生成新的数组

{…xx,xx:xxx}可以复制对象的属性时,对某个属性进行重新赋值操作

=====================脚手架中使用react

子组件如何如何给父组件传递数据

  • 使用回调函数

    • 父组件给子组件传递一个函数,子组件调用即可

    • //App.jsx
      //父组件定义好方法,然后将方法传递给子组件
      /*
      <Footer todos={todos} checkAllTodo={this.checkAllTodo} clearAllDone={this.clearAllDone}/>
      	//checkAllTodo用于全选
      	checkAllTodo = (done)=>{
      		//获取原来的todos
      		const {todos} = this.state
      		//加工数据
      		const newTodos = todos.map((todoObj)=>{
      			return {...todoObj,done}
      		})
      		//更新状态
      		this.setState({todos:newTodos})
      	}
      */
      //子组件调用
      /*
      <input type="checkbox" onChange={this.handleCheckAll} checked={doneCount === total && total !== 0 ? true : false}/>
      	//全选checkbox的回调
      	handleCheckAll = (event)=>{
      		this.props.checkAllTodo(event.target.checked)
      	}
      */
      
  • 使用消息订阅与发布

    • 这个不是react中的方法

    • 可以在任意组件中使用

    • //引入
      import PubSub from 'pubsub-js'
      //消息订阅
      /*
      this.token = PubSub.subscribe('atguigu',(_,stateObj)=>{
      			this.setState(stateObj)
      		})
      */
      //消息发布
      /*
      PubSub.publish('atguigu',{isLoading:false,users:response.data.items})
      */
      //取消消息订阅
      /*
      componentWillUnmount(){
      		PubSub.unsubscribe(this.token)
      	}
      */
      

路由的使用

路由的匹配规则:点击了某个路由链接后,url地址会变成改地址,然后history会监听到url地址的变化,然后到App.jsx中匹配路由规则,匹配到了后显示该组件,继续匹配该组件中的的路由规则…,所以这也是路由匹配规则中不能使用exact的原因

  • 基本使用

    • 路由链接组件比如Link,NavLink等最后会被渲染成a标签

    • App.jsx
      //引入
      import {Link,Route} from 'react-router-dom'
      {/* 在React中靠路由链接实现切换组件--编写路由链接 */}
      							<Link className="list-group-item" to="/about">About</Link>
      							<Link className="list-group-item" to="/home">Home</Link>
      {/* 注册路由 */}
      								<Route path="/about" component={About}/>
      								<Route path="/home" component={Home}/>
      index.js
      //引入react核心库
      import React from 'react'
      //引入ReactDOM
      import ReactDOM from 'react-dom'
      //
      import {BrowserRouter} from 'react-router-dom'
      //引入App
      import App from './App'
      //用BrowserRouter包裹着<App/>保证路由组件使用同一个Router
      ReactDOM.render(
      	<BrowserRouter>
      		<App/>
      	</BrowserRouter>,
      	document.getElementById('root')
      )
      
  • NavLink

    • 点击后,默认会给当前的a标签添加active类

    • 修改模拟的点击添加的类名,activeClassName=“atguigu”

    • 封装NavLink

      • MyNavLink
        /*
        <NavLink activeClassName="atguigu" className="list-group-item" {...this.props}/>
        */
        App.jsx
        //<MyNavLink to="/about">About</MyNavLink>
        
  • Switch的使用

    • 不加Switch组件,匹配到路由后,还会继续往下匹配

    • <Switch>
      									<Route path="/about" component={About}/>
      									<Route path="/home" component={Home}/>
      									<Route path="/home" component={Test}/>
      								</Switch>
      
  • 精准匹配和模糊匹配

    • 路由默认是模糊匹配
    • Route标签中使用exact可以实现精准匹配
    • 精准匹配不能滥用,如果是二级路由的话,使用其就会有问题
  • Redirect

    • 如果匹配不到路由,会进行重定向到默认路由

    • <Switch>
      									<Route path="/about" component={About}/>
      									<Route path="/home" component={Home}/>
      									<Redirect to="/about"/>
      								</Switch>
      
  • 嵌套路由的使用

  • 传递参数(params,search,state),包括编程式路由导航

    • Message中的index.jsx
      import React, { Component } from 'react'
      import Detail from './Detail'
      import {Link, Route} from 'react-router-dom'
      import { countSubscriptions } from 'pubsub-js'
      
      export default class Message extends Component {
          state={
            messagedata:[
              {id:1,title:'消息1'},
              {id:2,title:'消息2'},
              {id:3,title:'消息3'}
            ]
          }
          pushShow=(id,title)=>{
            // this.props.history.push(`/home/message/detail/${id}/${title}`)
            // this.props.history.push(`/home/message/detail?id=${id}&title=${title}`)
            this.props.history.push('/home/message/detail',{id,title})
          }
          replaceShow=(id,title)=>{
            // this.props.history.replace(`/home/message/detail/${id}/${title}`)
            this.props.history.replace(`/home/message/detail?id=${id}&title=${title}`)
            this.props.history.replace('/home/message/detail',{id,title})
          }
          back=()=>{
            this.props.history.goBack()
          }
          forward=()=>{
            this.props.history.goForward()
          }
          go=()=>{
            this.props.history.go(-2)
          }
          render() {
              return (
                  <ul>
                    {
                      this.state.messagedata.map(v=>{
                        return (
                          <li key={v.id}>
                            {/* params传递参数 */}
                            {/* <Link to={`/home/message/detail/${v.id}/${v.title}`}>{v.title}</Link> */}
                            {/* search传递参数 */}
                            {/* <Link to={`/home/message/detail?id=${v.id}&title=${v.title}`}>{v.title}</Link> */}
                            {/* state传递参数 */}
                            <Link to={{pathname:'/home/message/detail',state:{id:v.id,title:v.title}}}>{v.title}</Link>
                            <button onClick={()=>{this.pushShow(v.id,v.title)}}>pushShow</button>
                            <button onClick={()=>{this.replaceShow(v.id,v.title)}}>replaceShow</button>
                          </li>
                        )
                      })
                    }
                    <br/>
                    <div>
                      {/* prams传递参数 */}
                      {/* <Route path="/home/message/detail/:id/:title" component={Detail}></Route> */}
                      {/* search传递参数 */}
                      {/* <Route path="/home/message/detail" component={Detail}></Route> */}
                      {/* state传递参数 */}
                      <Route path="/home/message/detail" component={Detail}></Route>
                    </div>
                    <br/>
                    <div>
                      <button onClick={this.back}>back</button>
                      <button onClick={this.forward}>forward</button>
                      <button onClick={this.go}>go</button>
                    </div>
                  </ul>
              )
          }
      }
      //Message组件中的Detail中的jsx
      import React, { Component } from 'react'
      import qs from 'querystring'
      
      export default class Detail extends Component {
          state={
              detaildata:[
                  {id:1,content:'111'},
                  {id:2,content:'222'},
                  {id:3,content:'333'}
              ]
          }
          render() {
              console.log(this.props)
              // 接受params传递的参数
              // let {id,title}=this.props.match.params
              //接受并处理search传递的参数
              // let search=this.props.location.search.slice(1)
              // let {id,title}=qs.parse(search)
              //接受state传递的参数
              let {id,title}=this.props.location.state
              let obj=this.state.detaildata.find(v=>{
                  return v.id===parseInt(id)
              })
              return (
                  <div>
                      <ul>
                          <li>ID:{id}</li>
                          <li>TITLE:{title}</li>
                          <li>CONTENT:{obj.content}</li>
                      </ul>
                  </div>
              )
          }
      }
      
      
    • 由于params,search参数都在地址栏上,所以刷新页面肯定不会导致数据的丢失问题

    • state参数是存放在history中的,(BrowserRouter)刷新页面不会导致数据的丢失,(HashRouter)刷新页面会丢失数据

  • push与replace模式

    • 默认为push模式
    • 使用push会在history中推入一个历史记录
    • replace不会推入一个历史记录
    • 使用replace模式,<MyNavLink replace to="/about">About</MyNavLink>
  • withRouter的使用

    • 路由组件中可以获取到histtory的信息,但是无法给它传递属性
    • 一般组件没有路由组件中特有的API,比如获取history信息
    • withRouter可以加工一般组件,让一般组件具备路由组件所特有的API
    • export default withRouter(Header),返回一个新的组件

关于样式丢失的问题

在index.html中引入外部的样式使用./引入的,url地址为嵌套路由的url地址时,可能刷新页面后,导致样式丢失的问题

解决办法:

  • 使用/xxx
  • 使用%PUBLIC_URL%/xxxx
  • 使用HashRouter,因为HashRouter中的url地址中带#,而服务器是不看#后面的地址的

redux和react-redux的使用

redux不是react中的,redux是一种状态管理,可以存取组件中共有的属性,方便管理

三个重要文件:action,store,reducer

注意:redux状态的改变不会导致组件的更新,所以要监听其状态的改变

  • 基本使用
index.js
/*
store.subscribe(()=>{
	ReactDOM.render(<App/>,document.getElementById('root'))
})
*/
redux文件夹
store.js
/*
//引入createStore,专门用于创建redux中最为核心的store对象
import {createStore} from 'redux'
//引入为Count组件服务的reducer
import countReducer from './count_reducer'
//暴露store
export default createStore(countReducer)
*/
count_reducer.js
/*
const initState = 0 //初始化状态
export default function countReducer(preState=initState,action){
	// console.log(preState);
	//从action对象中获取:type、data
	const {type,data} = action
	//根据type决定如何加工数据
	switch (type) {
		case 'increment': //如果是加
			return preState + data
		case 'decrement': //若果是减
			return preState - data
		default:
			return preState
	}
}
*/
Count文件夹下
index.jsx
//import store from '../../redux/store'
//获取store中的state
//<h1>当前求和为:{store.getState()}</h1>
/*
	//更新store中的state
	//加法
	increment = ()=>{
		const {value} = this.selectNumber
		store.dispatch({type:'increment',data:value*1})
	}
*/
  • 优化(使用共有变量,将生成action的方法放入到action.js中)

    redux文件夹
    constant.js
    /* 
    该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时防止程序员单词写错
    */
    export const INCREMENT = 'increment'
    export const DECREMENT = 'decrement'
    count_action.js
    /* 
    该文件专门为Count组件生成action对象
    */
    import {INCREMENT,DECREMENT} from './constant'
    export const createIncrementAction = data => ({type:INCREMENT,data})
    export const createDecrementAction = data => ({type:DECREMENT,data})
    count_reducer.js
    /* 
    	1.该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
    	2.reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
    */
    import {INCREMENT,DECREMENT} from './constant'
    
    const initState = 0 //初始化状态
    export default function countReducer(preState=initState,action){
    	// console.log(preState);
    	//从action对象中获取:type、data
    	const {type,data} = action
    	//根据type决定如何加工数据
    	switch (type) {
    		case INCREMENT: //如果是加
    			return preState + data
    		case DECREMENT: //若果是减
    			return preState - data
    		default:
    			return preState
    	}
    }
    Count文件夹下
    index.jsx
    	//加法
    	increment = ()=>{
    		const {value} = this.selectNumber
    		store.dispatch(createIncrementAction(value*1))
    	}
    
  • 如何在异步中改变store中的state

    • 在组件中异步代码中去修改store中的state

      • 	Count文件夹下
            index.jsx
            //异步加
        	incrementAsync = ()=>{
        		const {value} = this.selectNumber
        		setTimeout(()=>{
        			store.dispatch(createIncrementAction(value*1))
        		},500)
        	}
        
    • 使用函数式action

      • Count文件夹下
        index.jsx
        //异步加
        	incrementAsync = ()=>{
        		const {value} = this.selectNumber
        		// setTimeout(()=>{
        			store.dispatch(createIncrementAsyncAction(value*1,500))
        		// },500)
        	}
        redux文件夹下
        count_action.js
        //异步action,就是指action的值为函数,异步action中一般都会调用同步action,异步action不是必须要用的。
        export const createIncrementAsyncAction = (data,time) => {
        	return (dispatch)=>{
        		setTimeout(()=>{
        			dispatch(createIncrementAction(data))
        		},time)
        	}
        }
        redux文件夹
        store.js
        //引入redux-thunk,用于支持异步action
        import thunk from 'redux-thunk'
        //暴露store
        export default createStore(countReducer,applyMiddleware(thunk))
        

======================================================

上面只是用了redux,每一个组件都能对store的state的进行操作,不方便管理,所以还要使用react-redux

react-redux中只有容器组件可以对sotre中的state进行操作,其ui组件不能,只能靠容器组件传递state属性和修改state的回调函数

  • react-redux基本使用

    • containers文件夹下
      //代表容器组件
      Count文件夹下
      index.jsx
      //引入Count的UI组件
      import CountUI from '../../components/Count'
      //引入action
      import {
      	createIncrementAction,
      	createDecrementAction,
      	createIncrementAsyncAction
      } from '../../redux/count_action'
      
      //引入connect用于连接UI组件与redux
      import {connect} from 'react-redux'
      
      /* 
      	1.mapStateToProps函数返回的是一个对象;
      	2.返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value
      	3.mapStateToProps用于传递状态
      */
      function mapStateToProps(state){
      	return {count:state}
      }
      
      /* 
      	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)(CountUI)
      components文件夹
      Count文件夹下
      //代表ui组件
      index.jsx
      //获取
      <h1>当前求和为:{this.props.count}</h1>
      //修改
      //奇数再加
      	incrementIfOdd = ()=>{
      		const {value} = this.selectNumber
      		if(this.props.count % 2 !== 0){
      			this.props.jia(value*1)
      		}
      	}
      	//异步加
      	incrementAsync = ()=>{
      		const {value} = this.selectNumber
      		this.props.jiaAsync(value*1,500)
      	}
          App.jsx
      import React, { Component } from 'react'
      import Count from './containers/Count'
      import store from './redux/store'
      
      export default class App extends Component {
      	render() {
      		return (
      			<div>
      				{/* 给容器组件传递store */}
      				<Count store={store} />
      			</div>
      		)
      	}
      }
      

上面的store中的reducer只有一个,直接用createStore(countReducer,applyMiddleware(thunk))加入了,这样store中的state就是单个属性,非对象,直接获取就可以了

  • react-redux的优化使用

    • 加入了react-redux不需要我们手动去订阅sotre中state改变的信息了,加入Provider可以一次性将store传递给多个容器对象,可以将容器对象和ui对象写在一起,mapDispatchToProps可以是一个对象,然后直接把生成action的方法传递给ui对象,react-redux会自动把ui对象生成的action提交到store中的

    • 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')
      )
      containers文件夹下
      Count文件夹
      index.jsx
      //使用connect()()创建并暴露一个Count的容器组件
      export default connect(
      	state => ({count:state}),
      
      	//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)
      
  • 多个容器组件实现数据共享

    • store中的state是一个对象,该对象中的默认属性值是由其对应的reducer提供的

    • containers文件夹
      Count/index.jsx
      //使用connect()()创建并暴露一个Count的容器组件
      export default connect(
      	state => ({
      		count:state.he,
      		renshu:state.rens.length
      	}),
      	{
      		jia:createIncrementAction,
      		jian:createDecrementAction,
      		jiaAsync:createIncrementAsyncAction,
      	}
      )(Count)
      Person/index.jsx
      export default connect(
      	state => ({yiduiren:state.rens,he:state.he}),//映射状态
      	{jiaYiRen:createAddPersonAction}//映射操作状态的方法
      )(Person)
      redux文件夹
      actions/person.js
      import {ADD_PERSON} from '../constant'
      
      //创建增加一个人的action动作对象
      export const createAddPersonAction = personObj => ({type:ADD_PERSON,data:personObj})
      reducers/person.js
      import {ADD_PERSON} from '../constant'
      reducers/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: //若是添加一个人
      			return [data,...preState]
      		default:
      			return preState
      	}
      }
      store.js
      //引入createStore,专门用于创建redux中最为核心的store对象
      import {createStore,applyMiddleware,combineReducers} from 'redux'
      //引入为Count组件服务的reducer
      import countReducer from './reducers/count'
      //引入为Count组件服务的reducer
      import personReducer from './reducers/person'
      //引入redux-thunk,用于支持异步action
      import thunk from 'redux-thunk'
      
      //汇总所有的reducer变为一个总的reducer
      const allReducer = combineReducers({
      	he:countReducer,
      	rens:personReducer
      })
      
      //暴露store
      export default createStore(allReducer,applyMiddleware(thunk))
      App.jsx
      import React, { Component } from 'react'
      import Count from './containers/Count'
      import Person from './containers/Person'
      
      export default class App extends Component {
      	render() {
      		return (
      			<div>
      				<Count/>
      				<hr/>
      				<Person/>
      			</div>
      		)
      	}
      }
      
      

注意reducer中的preState是一个数组或者对象的话,就要返回一个新的数组或者对象,因为redux会比较两者的地址,如果地址不变,改变是无效的

  • 使用react-redux开发者工具监测store中的状态改变

    • redux/store.js
      import {composeWithDevTools} from 'redux-devtools-extension'
      export default createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值