setState()
setState更新狀態(tài)的2種方式
- 對(duì)象式的setState
setState(stateChange,[callback])
1. stateChange為狀態(tài)改變對(duì)象
2. callback是可選的回調(diào)函數(shù),它在狀態(tài)更新完畢售碳,界面也更新后(render調(diào)用后)才被調(diào)用
- 函數(shù)式的setState
setState(updater, [callback])
1. updater為返回stateChange對(duì)象的函數(shù)
2. updater可以接收到state和props
3. callback是可選的回調(diào)函數(shù)搪锣,它在狀態(tài)更新耳鸯、界面更新后(render調(diào)用后)才被調(diào)用
state = { count: 0 }
add = () => {
// 對(duì)象式的setState
const { count } = this.state // 0
this.setState({ count: count + 1}, () => {
console.log(this.state.count) // 1
})
console.log(this.state.count) // 0
// 函數(shù)式編程
/*
this.setState((state,props) => {
return {
count: state.count + 1
}
})
**/
this.setState(state => ({count: state.count + 1}))
}
對(duì)象式的setState是函數(shù)式的setState的簡(jiǎn)寫方式
使用原則:
1.如果新狀態(tài)不依賴于原狀態(tài) ====> 使用對(duì)象方式
2.如果新狀態(tài)依賴于原狀態(tài) =====> 使用函數(shù)方式
3.如果需要在setState()執(zhí)行后獲取最新的狀態(tài)數(shù)據(jù)秸仙,需要在第二個(gè)callback函數(shù)中讀取
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 />}>
{/* 注冊(cè)組件 */}
<Route path="/about" component={About}>
<Route path="/home" component={Home}>
</Suspense>
Hooks
React Hook/Hooks是什么聂薪?
- Hook是React 16.8.0版本增加的新特性/語法
- 可以使在函數(shù)組件中使用state以及其他的React特性
三個(gè)常用的Hook
-
State Hook:React.useState()
State Hook 讓函數(shù)組件也可以有state狀態(tài)部逮,并進(jìn)行狀態(tài)數(shù)據(jù)的讀寫操作
語法:const [xxx, setXxx] = React.useState(initValue)
-
useState()說明
- 參數(shù):第一次初始化制定的值在內(nèi)部做換粗
- 返回值:包含2個(gè)元素的數(shù)組枷恕,第一個(gè)為內(nèi)部值當(dāng)前狀態(tài)值,第二個(gè)為更新狀態(tài)值的函數(shù)
-
setXxx()的2種寫法:
- setXxx(newValue):參數(shù)為非函數(shù)值寻咒,直接指定新的狀態(tài)值哮翘,內(nèi)部用其覆蓋原來的值
- setXxx(value => newValue):參數(shù)作為函數(shù),接受原本的狀態(tài)值毛秘,返回新的狀態(tài)值饭寺,內(nèi)部用其覆蓋原來的狀態(tài)值
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>當(dāng)前數(shù)值為{ count }</h2> <p>我的名字是:{ name }</p> <button onClick={add}>增加</button> <button onClick={changeName}>更改名稱</button> </div> ) } export default Test
-
Effect Hook:React.useEffect()
Effect Hook 可以讓你在函數(shù)組件中執(zhí)行副作用操作(用于模擬類組件中的生命周期鉤子)
-
React中的副作用操作
- 發(fā)送ajax操作
- 設(shè)置訂閱/啟動(dòng)定時(shí)器
- 手動(dòng)更改真實(shí)DOM
-
語法說明:
useEffect(() => { // 在此可以執(zhí)行任何帶副作用的操作 return () => { // 在此做一些收尾工作,比如:清除定時(shí)器/取消訂閱等 } },[stateValue]) // 如果制定的是[]叫挟,回調(diào)函數(shù)只會(huì)在第一次render()后執(zhí)行
-
可以把 useEffect Hook 看做如下三個(gè)函數(shù)的組合
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>當(dāng)前求和為{ count }</h2> <button onClick={unMount}>卸載組件</button> </div> ) }
-
Ref Hook
- Ref Hook 可以在函數(shù)組件中存儲(chǔ)/查找組件內(nèi)的標(biāo)簽或任意其他數(shù)據(jù)
- 語法:const refContainer = React.useRef()
- 作用:保存標(biāo)簽對(duì)象艰匙,功能與React.createRef() 一樣
function Test () {
const refContainer = React.useRef()
function show() {
alert(refContainer.current.value)
}
return(
<div>
<input type="text" ref={ refContainer } />
<button onClick={ show }>點(diǎn)擊提示當(dāng)前輸入內(nèi)容</button>
</div>
)
}
Fragment
可以不用必須有一個(gè)真實(shí)DOM根標(biāo)簽了
<Fragment>
<input type="test" />
<input type="test" />
</Fragment>
// 編譯后
<input type="test" />
<input type="test" />
Context
一種組件間通信方式,常用于[祖組件]與[后代組件]間通信霞揉,一般都用于封裝react插件
使用:
import React, { Component } from 'react'
// 創(chuàng)建一個(gè)Context對(duì)象
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>
)
}
}
// 函數(shù)組件旬薯、類式組件都可以
function C2 () {
return(
<div>
<h5>我是C2組件</h5>
<h5>Context接收到的
<Consumer>
{/* value 就是 context 中的 value 數(shù)據(jù) */}
{ value => `用戶名:${ value.userName },年齡:${ value.age }` }
</Consumer>
</h5>
</div>
)
}
組件優(yōu)化
component的2個(gè)問題
- 只要執(zhí)行setState(),即使不改變狀態(tài)數(shù)據(jù)适秩,組件也會(huì)重新render()
- 當(dāng)前組件重新render()绊序,會(huì)自動(dòng)重新render()子組件,即使沒使用父組件的任何數(shù)據(jù) ===> 效率低
效率高的做法
只有當(dāng)組件的state或props數(shù)據(jù)發(fā)生改變時(shí)才重新render()
原因
component中的shouldComponentUpdate()總是返回true
- 解決一:重寫shouldComponentUpdate()方法秽荞,比較新舊state或props數(shù)據(jù)骤公,如果有變化才返回true,如果沒有返回false
export default class Car extends Component {
state = { carName: '奔馳63' }
changeCar = () => {
this.setState({
carName: '邁巴赫'
})
}
shouldComponentUpdate(nextProps, nextState) {
console.log(this.props, this.state) // 當(dāng)前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>我的車名字時(shí):{carName}</p>
<button onClick={this.changeCar}>點(diǎn)擊換車</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數(shù)據(jù)有變化才返回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>我的車名字時(shí):{carName}</p>
<button onClick={this.changeCar}>點(diǎn)擊換車</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>
)
}
}
注意??: 只是進(jìn)行state和props數(shù)據(jù)的淺比較阶捆,如果只是數(shù)據(jù)對(duì)象內(nèi)部數(shù)據(jù)變了,返回false。不能直接修改state數(shù)據(jù)洒试,而是要產(chǎn)生新數(shù)據(jù)倍奢。項(xiàng)目中一般使用PureComponent來優(yōu)化
render props
-
children props:通過組件標(biāo)簽體傳入結(jié)構(gòu)
<A> <B></B> </A> { this.props.children } // 如果B組件需要A組件內(nèi)的數(shù)據(jù) ===> 做不到
-
Render props:通過組件標(biāo)簽屬性傳入結(jié)構(gòu),一般使用render函數(shù)屬性
<A render={ (data) => <C data ={ data }></C>}></A> // A 組件 { this.props.render(內(nèi)部state數(shù)據(jù)) } // C 組件 --- 讀取A組件傳入的數(shù)據(jù)顯示 { this.props.data }
Error boundary
錯(cuò)誤邊界(Error boundary):用來捕獲后代組件錯(cuò)誤垒棋,渲染出備用頁面
只能捕獲后代組件生命周期產(chǎn)生的錯(cuò)誤卒煞,不能捕獲自己組件產(chǎn)生的錯(cuò)誤和其他組件在合成事件、定時(shí)器中產(chǎn)生的錯(cuò)誤
使用方式
static getDerivedStateFromError(error){
console.log(error)
return {
hasError: true
}
}
componentDidCatch(error, info) {
// 統(tǒng)計(jì)頁面的錯(cuò)誤叼架,發(fā)送請(qǐng)求到服務(wù)端
console.log(error, info)
}