setState
setState用法
可以传入一个对象,也可以传入一个函数,,
setState是异步的,会将对象加入一个队列,在一定的时间之后进行统一渲染,,,所以多次调用setState去累加某一个值,是会有问题的
如果想获取异步之后的值,有第二个参数,传入一个函数,获取渲染后的值
this.setState({count:this.state.count+1},(res)=>{
console.log(res,"res")
})
当然也可以在生命周期函数 componentDidUpdate(prevProps,prevState,snapshot)
如果想获取每次setState设置的状态值,,可以传入一个函数,获取上一个状态的state和props:
changeCount(){
/**
* setState 是异步的,, 如果想要获取上一次state的状态
* 上一个state,,, 可以在,上一个state的状态修改,,
* 返回一个对象,,和之前的state合并,, 合并使用 Object.assign
*/
this.setState((prevState,prevProps)=>{
console.log(prevState.count)
return {
count:prevState.count+1
}
})
this.setState((prevState,prevProps)=>{
console.log(prevState.count)
return {
count:prevState.count+1
}
})
}
在react18之后 setState是异步的,在react18之前,在react的事件中,setState是异步的,在原生DOM事件中,比如addEventListener或者setTimeout中是同步的
在react18之后,默认所有的操作都放到了批处理中,,, 异步批渲染
如果希望代码同步,,,可以使用 flushSync
import {flushSync} from "react-dom";
flushSync(()=>{
// 同步代码
this.setState({
count:11
})
})
console.log(this.state.count)
SCU
SCU: shouldComponentUpdate
默认继承react的Component类实现的组件,,在更新的时候,不管是否有改动,都会全部更新,,
shouldComponentUpdate 可以先浅层比较传入对象是否相同,相同就不去渲染,不相同再渲染。。。。
PureComponent是实现了这个SCU的组件,一般使用这个
react更新流程
- props/state改变
- render函数重新执行
- 产生新的dom树
- 进行diff算法
- 计算出差异,进行更新
这个diff算法最终被优化成了:
- 同层节点之间相互比较,不回跨节点比较
- 不同类型的节点,产生不同的树结构
- 开发中,可以通过key,来指定哪些节点在不同的渲染下保持稳定
shouldComponentUpdate
生命周期钩子
sholdComponentUpdate((nextProps,nextState)=>{
// 返回true, 调用render
// 返回false 不调用
})
类组件继承PureComponent可以自己比较
函数组件需要使用一个高阶函数 memo
进行包裹,,
高阶函数就是,传入一个组件,返回一个新的组件,,,对原来组件的增强
memo(function (){
return "xx"
})
不可变数据的力量
react组件中,,state中的数据,是不可变的,,不要去随意改变state中的数据,,而是浅拷贝一个新的值,修改之后去赋值
如果你直接修改state中的某个对象,,但是PureComponent或者Memo的浅层比较是发现不了对象底层数据变化的,
只能通过赋值一个新的对象,进行改变
ref使用
this.refs.名字
class App extends PureComponent {
componentDidMount() {
this.refs.hehe.printHehe()
}
render() {
return (
<div>
<Hehe ref="hehe"/>
</div>
);
}
}
createRef 函数
import React, { createRef, PureComponent} from 'react';
import Hehe from "./Hehe";
class App extends PureComponent {
componentDidMount() {
console.log(this.HeheRef)
this.HeheRef.current.printHehe()
}
constructor() {
super();
this.HeheRef = createRef()
}
render() {
return (
<div>
<Hehe ref={this.HeheRef}/>
</div>
);
}
}
export default App;
传入一个箭头函数,,函数的参数就是 当前ref本身,
import React, { createRef, PureComponent} from 'react';
import Hehe from "./Hehe";
class App extends PureComponent {
componentDidMount() {
this.heheRef.printHehe()
}
constructor() {
super();
this.heheRef = null
}
render() {
return (
<div>
<Hehe ref={(result)=>this.heheRef = result}/>
</div>
);
}
}
export default App;
函数式组件没有实例,无法通过ref获取他们的实例。但是某些时候,我们想获取函数式组件内的某个DOM,,
可以将ref传递进去
import {forwardRef} from "react";
const HelloWorld = forwardRef(function(props,ref){
return (
<div>
<h2 ref={ref}>hello word</h2>
</div>
)
})
export default HelloWorld
import React, { createRef, PureComponent} from 'react';
import Hehe from "./Hehe";
import HelloWorld from "./HelloWorld";
class App extends PureComponent {
componentDidMount() {
this.heheRef.printHehe()
console.log(this.hellowordRef.current)
}
constructor() {
super();
this.heheRef = null
this.hellowordRef = createRef()
}
render() {
return (
<div>
<Hehe ref={(result)=>this.heheRef = result}/>
<HelloWorld ref={this.hellowordRef}/>
</div>
);
}
}
export default App;
受控组件
受控组件: 受react控制的组件,,如果绑定了react的state,就被react控制了,需要通过onChange去改变这个input的值。。
react中,state属性,只能通过setState来更新
非受控组件: 没有被react控制的组件,,使用defaultValue设置默认值,,用原生的方式去获取数据
js表单控件改变会掺入一个event,, event.target
就是这个控件,, 设置state的时候,可以写成:
// 通过 event.target.type 先判断一个是否是 checkbox 或者是 radio,,
// 因为这两个控件的值,,在event.target.checked 上
this.setState({
[event.state.name]:event.state.value
})
checkbox的多选,和 select的多选
高阶组件
高阶函数
- 接受一个或者多个函数作为输入
- 或者 返回一个函数
比如 : map ,filter,reduce
高阶组件 higher order components
高阶组件,本身不是一个组件,是一个函数,,并且这个函数的参数是另一个组件,,返回值也是一个组件
props增强
有时候多个组件会共用属性,,但是每一个去使用 <Context.Consumer>
代码会很冗余,,在高阶函数中处理这些共用的属性
import React from "react";
const UserContext = React.createContext()
export default UserContext
import UserContext from "../context/user-context";
function enhancerProps(OriginComponent){
// 内部是另一个组件 === props是父组件传过来的props
return (props)=>{
return (
<UserContext.Consumer>
{ value => <OriginComponent {...props} {...value} />}
</UserContext.Consumer>
)
}
}
export default enhancerProps
import React, { PureComponent} from 'react';
import HelloWorld from "./HelloWorld";
import UserContext from "./context/user-context";
class App extends PureComponent {
constructor() {
super();
this.state = {
userInfo:{
username:"cc",
age:11
}
}
}
render() {
return (
<div>
<UserContext.Provider value={this.state.userInfo}>
<HelloWorld address="成都"/>
</UserContext.Provider>
</div>
);
}
}
export default App;
import React, {PureComponent} from 'react';
import enhancerProps from "./hoc/enhancerProps";
import UserContext from "./context/user-context";
class HelloWorld extends PureComponent {
render() {
return (
<div>
{/*<UserContext.Consumer>*/}
{/* { value=> <h3>{value.username}</h3>}*/}
{/*</UserContext.Consumer>*/}
{ this.props.username} - {this.props.age} --{this.props.address}
</div>
);
}
}
export default enhancerProps(HelloWorld);
判断是否登录,,或者判断数据是否为空,展示提示
写在高阶组件里面
这个状态改变之后,是根据localStorage中的判断的,没有设置setState,也就没有执行render,, this.forceUpdate()
强制更新
生命周期劫持
比如判断某个组件的加载时间,在钩子函数 UNSAFE_componentWillMount
和 componentDidMount
记录时间差
显示组件的加载时间
Hook可以替代HOC
portals使用
portals: 大门
react中会设置一个根组件,下面的子元素默认都是在这个根组件下面的,,, 在处理,对话框,提示框,固定定位元素的时候非常有用
import {createPortal} from "react-dom";
// 将第一个参数传入的节点,放入指定的DOM中
{createPortal(<div>hehe123</div>,document.querySelector("#other"))}
Fragment
类似于vue中的 template,。。。
<Fragment></Fragment>
react中的严格模式
// 被strictMode 标签包裹就会使用严格模式
<StrictMode>
<App/>
</StrictMode>