扩展
setState
- (1). setState(stateChange, [callback])------对象式的setState
- 1.stateChange为状态改变对象(该对象可以体现出状态的更改)
- 2.callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用
- (2). setState(updater, [callback])------函数式的setState
- 1.updater为返回stateChange对象的函数。
- 2.updater可以接收到state和props。
- 3.callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。
- 总结:
- 1.对象式的setState是函数式的setState的简写方式(语法糖)
- 2.使用原则:
- (1).如果新状态不依赖于原状态 ===> 使用对象方式
- (2).如果新状态依赖于原状态 ===> 使用函数方式
- (3).如果需要在setState()执行后获取最新的状态数据,
- 要在第二个callback函数中读取
LazyLoader
路由组件的懒加载
- # 导入库
- import React, {lazy,Suspense} from 'react';
- //1.通过React的lazy函数配合import()函数动态加载路由组件 ===> 路由组件代码会被分开打包
- const Login = lazy(()=>import('@/pages/Login'))
-
- //2.通过<Suspense>指定在加载得到路由打包文件前显示一个自定义loading界面
- <Suspense fallback={<h1>loading.....</h1>}>
- <Switch>
- <Route path="/xxx" component={Xxxx}/>
- <Redirect to="/login"/>
- </Switch>
- </Suspense>
Hooks
简介
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
useState
- /**
- * 使用范围: 用于函数式组件, 使函数式组件具备state的能力
- * useState的使用方式
- * 1: 从react库中引入 useState 函数
- * 2: 使用函数创建值引用和方法引用
- * 2.1: const [count, setCount] = useState(0)
- * 2.2: 调用useState 入参为初次属性初始化的默认值
- * 2.3: 返回值为数组,一般使用结构的方式获取回来, 第一个引用为值对象, 第二个引用为该值对象的赋值函数
- * 3: 渲染方式, 直接通过 {count} 渲染
- * 4: 赋值方式: 调用赋值函数
- * 4.1: 入参为值对象修改 setCount(count+1)
- * 4.2: 入参为函数修改: setCount(count => count + 1) 函数会有一个入参为当前值对象, 然后需要返回一个新的值对象
- */
- import React, {useState} from 'react';
-
- function Index(props) {
- const [count, setCount] = useState(0)
-
- const add = () => {
- // setCount(count+1)
- setCount(count => count + 1)
- }
- return (
- <div>
- <h2>当前求和为:{count}</h2>
- <button onClick={add}>+1</button>
- </div>
- );
- }
-
- export default Index;
useEffect
- /**
- * 使用范围: 用于函数式组件, 使函数式组件具备生命周期钩子的能力,可以看做是
- * componentDidMount,componentDidUpdate,componentWillUnmount
- * 三个生命周期钩子函数的集合
- * useEffect的使用方式
- * 1: 从react库中引入 useEffect 函数
- * 2: 使用函数完成生命周期钩子函数
- * -:具体使用看下面注释
- *
- */
- import React, {useState, useEffect} from 'react';
-
- function Index(props) {
-
- // useState
- const [count, setCount] = useState(0)
- const [sum, setSum] = useState(0)
- const [he, setHe] = useState(0)
-
- /**
- * 实现componentDidMount
- * useEffect 第二个参数[] 什么也不写, 就是代表不监听任何state的变化, 只有在第一次渲染的时候执行
- */
- useEffect(() => {
- // setCount(count+1)
- // 实现count自动累加
- const timer = setInterval(() => {
- // 这里有个问题, 需要使用函数式入参, 不能直接使用值入参, 因为值入参是异步的, 函数的话会接受到上一次的值
- setCount(count => count + 1)
- }, 1000)
- }, [])
-
- /**
- * 实现componentDidMount+componentDidUpdate
- * useEffect 第二个参数[] 里面写了那些state的值对象, 当这些值对象发生变化时,就会执行这个函数
- */
- useEffect(() => {
- // 当count改变的时候sum自动加1
- if (count !== 0) {
- setSum(sum => sum + 1)
- }
- }, [count])
-
- /**
- * 实现componentDidMount+componentDidUpdate+componentWillUnmount
- * useEffect 函数, 可以返回一个函数, 这个返回的函数就是componentWillUnmount生命周期钩子, 所有清除定时器,取消订阅等操作就可以写在这个函数里面
- */
- useEffect(() => {
- // 当count改变的时候sum自动加1
- const timer = setInterval(() => {
- // 这里有个问题, 需要使用函数式入参, 不能直接使用值入参, 因为值入参是异步的, 函数的话会接受到上一次的值
- setHe(he => he + 1)
- }, 1000)
- return () => {
- clearInterval(timer)
- }
- }, [])
-
- return (
- <div>
- <h2>当前求和为:{count}</h2>
- <h2>当前求和为:{sum}</h2>
- <h2>当前求和为:{he}</h2>
- </div>
- );
- }
-
- export default Index;
useRef
- /**
- * 使用范围: 用于函数式组件, 使函数式组件具备React.createRef的能力
- * useRef的使用方式
- * 1: 从react库中引入useRef函数
- * 2: 使用函数创建属性 const myRef = useRef()
- * 3: 绑定到组件
- * 4: 获取值 myRef.current.value
- */
- import React, {useRef} from 'react';
-
- function Index(props) {
-
- const myRef = useRef()
-
- const show = () => {
- console.log(myRef.current.value)
- }
-
- return (
- <div>
- <input ref={myRef} type="text"/>
- <button onClick={show}>显示</button>
- </div>
- );
- }
-
- export default Index;
Fragment
- /**
- * Fragment : 代码片段标签, 在React渲染时会被丢弃
- * 使用方式:
- * 1: 从react库中引入
- * 2: 一般包裹在最外层
- * 3: 只接受唯一一个属性 key
- * 4: 如不过想写, 可以使用空标签替换 <>>
- */
- import React, {Fragment} from 'react';
-
- function Index(props) {
- return (
- <Fragment key={'fg'}>
- <h2>代码片段:</h2>
- </Fragment>
- );
- }
-
- export default Index;
Context+useContext(Hooks)
- /**
- * Context: 上下文对象, 一般用于多层次组件传递值
- * 使用方式:
- * 1: 从react中引入React
- * 2: 创建: const UserNameContext = createContext('dance')
- * -: 看下方注释
- */
- import React, {Component, useState, useContext, createContext} from 'react';
-
- // 创建Context对象
- const UserNameContext = createContext('dance')
-
- function Main(props) {
- const [userName, setUserName] = useState('tom');
- return (
- <>
- <h2>我是Main组件</h2>
- <h3>我的用户名是:{userName}</h3>
- <hr/>
- {/* 通过value属性传入参数, 所有的子组件就都可以获取到context */}
- <UserNameContext.Provider value={userName}>
- <A/>
- </UserNameContext.Provider>
- </>
- );
- }
-
- function A(props) {
- return (
- <>
- <h2>我是A组件</h2>
- {/* 通过标签Consumer获取,并渲染 */}
- <UserNameContext.Consumer>
- {
- value => (<h3>接受到的用户名是:{value}</h3>)
- }
- </UserNameContext.Consumer>
- <hr/>
- <B/>
- </>
- );
- }
-
- /**
- * 函数式组件接受Context
- */
- function B(props) {
- // 通过useContext函数获取,并渲染
- let context = useContext(UserNameContext)
- return (
- <>
- <h2>我是B组件</h2>
- <h3>接受到的用户名是:{context}</h3>
- <C/>
- </>
- );
- }
-
- /**
- * 类组件 接受Context
- */
- class C extends Component {
- // 类组件通过属性获取,并渲染
- static contextType = UserNameContext
-
- render() {
- return (
- <>
- <h2>我是C组件</h2>
- <h3>接受到的用户名是:{this.context}</h3>
- </>
- );
- }
- }
-
- export default Main;
组件优化
Component的两个问题
- 只要执行setState(),即使不改变状态数据, 组件也会重新render() ==> 效率低
- 只当前组件重新render(), 就会自动重新render子组件,纵使子组件没有用到父组件的任何数据 ==> 效率低
优化
要让组件, 只有当组件的state或props数据发生改变时才重新render()
因为Component中的shouldComponentUpdate()总是返回true
解决办法
- 办法1:
- 重写shouldComponentUpdate()方法
- 比较新旧state或props数据, 如果有变化才返回true, 如果没有返回false
- 办法2:
- 使用PureComponent
- PureComponent重写了shouldComponentUpdate(), 只有state或props数据有变化才返回true
- 注意:
- 只是进行state和props数据的浅比较, 如果只是数据对象内部数据变了, 返回false
- 不要直接修改state数据, 而是要产生新数据
- 项目中一般使用PureComponent来优化
但是一般项目开发中都是用函数式组件+hooks来写的
render props
如何向组件内部动态传入带内容的结构(标签)?
- Vue中:
- 使用slot技术, 也就是通过组件标签体传入结构 <A><B/></A>
- React中:
- 使用children props: 通过组件标签体传入结构
- 使用render props: 通过组件标签属性传入结构,而且可以携带数据,一般用render函数属性
children props
- <A>
- <B>xxxx</B>
- </A>
- {this.props.children}
- 问题: 如果B组件需要A组件内的数据, ==> 做不到
render props
- <A render={(data) => <C data={data}></C>}></A>
- A组件: {this.props.render(内部state数据)}
- C组件: 读取A组件传入的数据显示 {this.props.data}
错误边界
- 理解:
- 错误边界(Error boundary):用来捕获后代组件错误,渲染出备用页面
- 特点:
- 只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误
- 使用方式:
- getDerivedStateFromError配合componentDidCatch
- // 生命周期函数,一旦后台组件报错,就会触发
- static getDerivedStateFromError(error) {
- console.log(error);
- // 在render之前触发
- // 返回新的state
- return {
- hasError: true,
- };
- }
-
- componentDidCatch(error, info) {
- // 统计页面的错误。发送请求发送到后台去
- console.log(error, info);
- }
组件通信方式总结
组件间的关系:
- 父子组件
- 兄弟组件(非嵌套组件)
- 祖孙组件(跨级组件)
几种通信方式:
- 1.props:
- (1).children props
- (2).render props
- 2.消息订阅-发布:
- pubs-sub、event等等
- 3.集中式管理:
- redux、dva等等
- 4.conText:
- 生产者-消费者模式
比较好的搭配方式:
- 父子组件:props
- 兄弟组件:消息订阅-发布、集中式管理
- 祖孙组件(跨级组件):消息订阅-发布、集中式管理、conText(开发用的少,封装插件用的多)