React学习06- API扩展
文章目录
- setState
- lazyLoad
- Hooks
- 三个常用的Hook
- React.useState()
- React.useEffect()
- React.useRef()
- Fragment
- Context
- 组件优化
- render props
- 错误边界
- 组件通信方式总结
setState
更新状态的两种写法
setState(stateChange,[callback]) // 传入对象参数,callback为可选的回调函数,在状态、页面都更新,render调用之后,才被调用
setState(updater,[callback]) // 传入方法参数,updater是返回一个状态对象的函数,可以接收到state和props
setState是异步更新,setState是同步的,它之后做的更新动作是异步的
// 先打印旧count,然后再+1
this.setState({count:count+1})
console.log(this.state.count)
// 等待更新之后打印+1
this.setState({count:count+1},()=>{
console.log(this.state.count)
})
// 函数式setState
this.setState((state,props)=>{
return {count:state.count+1}
})
如果新状态不依赖于原状态,使用对象式;
如果新状态依赖原状态,使用函数式;
如果需要在setState执行之后获取最新的状态数据,需要在第二个参数callback函数中获取。
lazyLoad
路由组件的懒加载,当页面使用路由导航,第一次加载页面时会把所有路由组件都请求一次,懒加载可以做到需要哪个再请求哪个。
import {lazy,Suspense} from 'react'
import Loading from './Loading'
// 不使用import,使用定义变量的形式引入路由组件
const Demo = lazy(()=>import('./Demo'))
// 使用Suspense包裹路由组件,当点击导航还未返回结果时显示fallback里的虚拟DOM
<Suspense fallback={<Loading/>}>
<Route path="/demo" component={Demo}/>
</Suspense>
Hooks
Hook是React16.8.0新增的语法特性,函数式组件没有自己的this,所以很多特性都用不了,在16.8以后的版本可以在函数组件中使用state和其他React特性。
三个常用的Hook
State Hook: React.useState()
Effect Hook: React.useEffect()
Ref Hook: React.useRef()
React.useState()
useState()的参数在第一次初始化时指定的值在内部做缓存,返回值是包括2个元素的数组,第一个是内部当前的状态,第二个是更新状态的函数。
const [状态变量,更新状态变量的方法] = React.useState(状态变量初始值)
// 更新状态变量的方法,两种写法
setXXX(newValue)
setXXX(value => newValue)
function Demo(){
console.log('Demo') //点击+1按钮,Demo就执行一次
const [count,setCount] = React.useState(0) // React底层做了处理,不会重复执行
function add(){
// setCount(count+1)
setCount(count => count+1)
}
return (
<div>
<h1>当前和:{count}</h1>
<button onClick={add}>点击+1</button>
</div>
)
}
React.useEffect()
useEffect类似监听器,可以用于模拟生命周期钩子,可以看作是componentDidMount(), componentDidUpdate(), componentWillUnmount()的组合
useEffect(()=>{
// 在此执行副作用操作(发送ajax请求,订阅,启动定时器,手动修改真实DOM...)
...componentDidMount(), componentDidUpdate()
return ()=>{
// 在此做收尾工作 componentWillUnmount
}
}, [监视的变量]) // 如果指定为[],回调函数只会在第一次render()后执行;当监视的变量有更新时,调用一次useEffect
React.useEffect(()=>{
let timer = setInterval(()=>{
setCount(count => count+1)
},1000)
return ()=>{
clearInterval(timer)
}
},[count])
React.useRef()
useRef可以在函数式组件中存储/查找组件内标签或其他数据,用于保存标签对象,功能和React.createRef()一样
function Demo(){
const myRef = React.useRef()
function show(){
alert(myRef.current.value)
}
return (
<div>
<input type="text" ref={myRef}>
<button onClick={show}>展示输入值</button>
</div>
)
}
Fragment
Fragment用于代替组件中的根标签
<Fragment></Fragment>
<></> // 也可以使用空标签代替
render(){
return (
<Fragment>
<input type="text"/>
</Fragment>
)
}
Context
context用于组件与子孙组件之间的通信,开发中一般不用context,一般在开发react插件时使用
// 1.先创建Context对象,要放在要通信的组件都能访问到的地方
const MyContext = React.createContext()
class A extends Component{
state = {name:'tom'}
render(){
return (
<>
<h1>A: {this.state.name}</h1>
<MyContext.Provider value={name}>
<B/>
</MyContext.Provider> // 2. 被MyContext包裹的组件及其子组件都能使用传递的value
</>
)
}
}
class B extends Component{
render(){
return (
<>
<h1>B: </h1>
<C/>
</>
)
}
}
class C extends Component{
static contextType = MyContext // 3.方法1. 必须声明才能使用
render(){
return (
<>
<h1>C: {this.context}</h1> // 4.方法1.使用
</>
)
}
}
function C(){
return (
<>
<h1>C:
<MyContext.Consumer>
{
value => {
return `${value.name}`
}
}
</MyCOntext.Consumer>
</h1> // 4.方法2.使用
</>
)
}
组件优化
Component的两个问题:
1.只要执行setState(),即使不改变状态数据,组件也会重新render()
2.只当前组件重新render(),就会自动重新render子组件,效率低
因为组件中的shouldComponentUpdate()总是返回true。
做到只要当组件的state或props数据发生改变时才重新render(),
办法1,重写shouldComponentUpdate(),比较新旧state或props数据,有变化才返回true,没有则返回false。
shouldComponentUpdtae(newProps,newState){
if(oldState!=newState)return true;
return false;
}
办法2,使用PureComponent,PureComponent重写了shouldComponentUpdate(),只有state或props数据有变化才返回true。
注意只是进行了state和props数据的浅比较,如果只是数据对象内部数据变了,返回false,不要直接修改state数据,而是要产生新数据。
import {PureComponent} from 'react'
class A extends PureComponent{...}
render props
类似Vue的插槽,在组件内预留一个位置,可以显示需要的其他组件。
<A>hello</A> // 在页面不会显示hello,需要在A组件中使用this.props.children
export default class Parent extends Component{
render(){
return(
<div>
<A><B/></A> // 不在A组件内使用B组件形成父子关系
</div>
)
}
}
class A extends Component{
render(){
return (
<div>
{this.props.children}// 必须在A组件中需要显示B组件的位置使用这行语句,才可以显示B组件,
</div>
)
}
}
export default class Parent extends Component{
render(){
return(
<div>
<A render={(name) => <B name={name} />}/> // 箭头函数返回一个组件
</div>
)
}
}
class A extends Component{
state = {name:'tom'}
render(){
return (
<div>
A组件内有: {this.props.render(name)} // 将Parent中,A组件里的箭头函数体内容显示在这里,这里是B组件,并传递了A组件的值给B组件去显示
</div>
)
}
}
class B extends Component{
render(){
return (
<div>
A组件的数据:{this.props.name}
</div>
)
}
}
错误边界
用于捕获后代组件错误,渲染出备用页面。只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误。
// 生命周期函数,一旦后台组件报错就会触发,在容易报错的组件的父组件使用
static getDrivedStateFromError(error){
console.log(error) // 错误信息
// 在render之前触发,返回新的state
return {hasError: true}
}
componentDidCatch(error,info){
// 统计页面的错误,发送请求到后台
console.log(error,info)
}
组件通信方式总结
- props(父子组件): (1) children props (2) render props
- 消息订阅发布(兄弟组件、跨级组件): pubs-sub
- 集中式管理(兄弟组件、跨级组件): redux
- conText(跨级组件):生产者-消费者模式