setState()
setState更新状态的2种方式
- 对象式的setState
setState(stateChange,[callback])
1. stateChange为状态改变对象
2. callback是可选的回调函数,它在状态更新完毕,界面也更新后(render调用后)才被调用
- 函数式的setState
setState(updater, [callback])
1. updater为返回stateChange对象的函数
2. updater可以接收到state和props
3. callback是可选的回调函数,它在状态更新、界面更新后(render调用后)才被调用
state = { count: 0 }
add = () => {
// 对象式的setState
const { count } = this.state // 0
this.setState({ count: count + 1}, () => {
console.log(this.state.count) // 1
})
console.log(this.state.count) // 0
// 函数式编程
/*
this.setState((state,props) => {
return {
count: state.count + 1
}
})
**/
this.setState(state => ({count: state.count + 1}))
}
对象式的setState是函数式的setState的简写方式
使用原则:
1.如果新状态不依赖于原状态 ====> 使用对象方式
2.如果新状态依赖于原状态 =====> 使用函数方式
3.如果需要在setState()执行后获取最新的状态数据,需要在第二个callback函数中读取
lazyLoad
路由组件的lazyLoad
import React, { Component, lazy, Suspense } from 'react'
const Loading from './Loading'
const Home = lazy(() => import('./Home'))
const About = lazy(() => import('./About'))
<Suspense fallback={<Loading />}>
{/* 注册组件 */}
<Route path="/about" component={About}>
<Route path="/home" component={Home}>
</Suspense>
Hooks
React Hook/Hooks是什么?
- Hook是React 16.8.0版本增加的新特性/语法
- 可以使在函数组件中使用state以及其他的React特性
三个常用的Hook
-
State Hook:React.useState()
State Hook 让函数组件也可以有state状态,并进行状态数据的读写操作
语法:const [xxx, setXxx] = React.useState(initValue)
-
useState()说明
- 参数:第一次初始化制定的值在内部做换粗
- 返回值:包含2个元素的数组,第一个为内部值当前状态值,第二个为更新状态值的函数
-
setXxx()的2种写法:
- setXxx(newValue):参数为非函数值,直接指定新的状态值,内部用其覆盖原来的值
- setXxx(value => newValue):参数作为函数,接受原本的状态值,返回新的状态值,内部用其覆盖原来的状态值
function Test () { console.log('Test组件') const [count, setCount] = React.useState(0) const [name, setName] = React.useState('tom') function add () { // setCount(count + 1) // 第一种写法 setCount(count => count + 1) } function changeName() { setName('jack') } return( <div> <h1>Hooks</h1> <h2>当前数值为{ count }</h2> <p>我的名字是:{ name }</p> <button onClick={add}>增加</button> <button onClick={changeName}>更改名称</button> </div> ) } export default Test
-
Effect Hook:React.useEffect()
Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)
-
React中的副作用操作
- 发送ajax操作
- 设置订阅/启动定时器
- 手动更改真实DOM
-
语法说明:
useEffect(() => { // 在此可以执行任何带副作用的操作 return () => { // 在此做一些收尾工作,比如:清除定时器/取消订阅等 } },[stateValue]) // 如果制定的是[],回调函数只会在第一次render()后执行
-
可以把 useEffect Hook 看做如下三个函数的组合
componentDidMount() componentDidUpdate() componentWillUnmount()
function Test () { console.log('Test组件') const [count, setCount] = React.useState(0) React.useEffect(() => { let timer = setInterval(() => { setCount(count => count + 1) }, 1000); return () => { console.log('卸载组件####') clearInterval(timer) } },[]) function unMount() { reactDom.unmountComponentAtNode(document.getElementById('root')) } return( <div> <h1>Hooks</h1> <h2>当前求和为{ count }</h2> <button onClick={unMount}>卸载组件</button> </div> ) }
-
Ref Hook
- Ref Hook 可以在函数组件中存储/查找组件内的标签或任意其他数据
- 语法:const refContainer = React.useRef()
- 作用:保存标签对象,功能与React.createRef() 一样
function Test () {
const refContainer = React.useRef()
function show() {
alert(refContainer.current.value)
}
return(
<div>
<input type="text" ref={ refContainer } />
<button onClick={ show }>点击提示当前输入内容</button>
</div>
)
}
Fragment
可以不用必须有一个真实DOM根标签了
<Fragment>
<input type="test" />
<input type="test" />
</Fragment>
// 编译后
<input type="test" />
<input type="test" />
Context
一种组件间通信方式,常用于[祖组件]与[后代组件]间通信,一般都用于封装react插件
使用:
import React, { Component } from 'react'
// 创建一个Context对象
const MyContext = React.createContext()
const { Provider, Consumer } = MyContext
export default class ContextComponent extends Component {
state = { userName: '张三', age: 18 }
render() {
const { userName, age } = this.state
return (
<div>
<h2>Context</h2>
<Provider value={{userName, age}}>
<A />
</Provider>
</div>
)
}
}
class A extends Component {
render() {
return(
<div>
<h3>我是a组件</h3>
<B />
</div>
)
}
}
class B extends Component {
state = { userName: 'tom' }
render() {
return(
<div>
<h4>我是B组件</h4>
<C1 />
<C2 />
</div>
)
}
}
// 类式组件
class C1 extends Component {
// 声明context
static contextType = MyContext
state = { userName: 'tom' }
render() {
return(
<div>
<h5>我是C1组件</h5>
<h5>Context接收到的用户名:{ this.context.userName }, 年龄:{ this.context.age }</h5>
</div>
)
}
}
// 函数组件、类式组件都可以
function C2 () {
return(
<div>
<h5>我是C2组件</h5>
<h5>Context接收到的
<Consumer>
{/* value 就是 context 中的 value 数据 */}
{ value => `用户名:${ value.userName },年龄:${ value.age }` }
</Consumer>
</h5>
</div>
)
}
组件优化
component的2个问题
- 只要执行setState(),即使不改变状态数据,组件也会重新render()
- 当前组件重新render(),会自动重新render()子组件,即使没使用父组件的任何数据 ===> 效率低
效率高的做法
只有当组件的state或props数据发生改变时才重新render()
原因
component中的shouldComponentUpdate()总是返回true
- 解决一:重写shouldComponentUpdate()方法,比较新旧state或props数据,如果有变化才返回true,如果没有返回false
export default class Car extends Component {
state = { carName: '奔驰63' }
changeCar = () => {
this.setState({
carName: '迈巴赫'
})
}
shouldComponentUpdate(nextProps, nextState) {
console.log(this.props, this.state) // 当前props和state
console.log(nextProps, nextState) // 接下来要变化的props和state
return !this.state.carName === nextState.carName
}
render() {
console.log('Car-parent render')
const { carName } = this.state
return (
<div>
<h3>我是Parent组件</h3>
<p>我的车名字时:{carName}</p>
<button onClick={this.changeCar}>点击换车</button>
<Child carName={carName} />
</div>
)
}
}
class Child extends Component {
render() {
console.log('Car-child render')
const { carName } = this.props
return (
<div>
<h4>我是Child组件</h4>
<p>我接到的车是:{ carName }</p>
</div>
)
}
}
- 解决二:使用PureComponent,PureComponent重写了shouldComponentUpdate(),只有state或props数据有变化才返回true
import React, { PureComponent } from 'react'
export default class Car extends PureComponent {
state = { carName: '奔驰63' }
changeCar = () => {
this.setState({
carName: '迈巴赫'
})
}
render() {
console.log('Car-parent render')
const { carName } = this.state
return (
<div>
<h3>我是Parent组件</h3>
<p>我的车名字时:{carName}</p>
<button onClick={this.changeCar}>点击换车</button>
<Child carName={carName} />
</div>
)
}
}
class Child extends PureComponent {
render() {
console.log('Car-child render')
const { carName } = this.props
return (
<div>
<h4>我是Child组件</h4>
<p>我接到的车是:{ carName }</p>
</div>
)
}
}
注意⚠️: 只是进行state和props数据的浅比较,如果只是数据对象内部数据变了,返回false。不能直接修改state数据,而是要产生新数据。项目中一般使用PureComponent来优化
render props
-
children props:通过组件标签体传入结构
<A> <B></B> </A> { this.props.children } // 如果B组件需要A组件内的数据 ===> 做不到
-
Render props:通过组件标签属性传入结构,一般使用render函数属性
<A render={ (data) => <C data ={ data }></C>}></A> // A 组件 { this.props.render(内部state数据) } // C 组件 --- 读取A组件传入的数据显示 { this.props.data }
Error boundary
错误边界(Error boundary):用来捕获后代组件错误,渲染出备用页面
只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误
使用方式
static getDerivedStateFromError(error){
console.log(error)
return {
hasError: true
}
}
componentDidCatch(error, info) {
// 统计页面的错误,发送请求到服务端
console.log(error, info)
}