state【自动批处理、useState与useReducer、受控组件与非受控组件】

14 篇文章 0 订阅

目录

定义:组件的私有属性,异步更新的响应式数据

自动批处理:合并多个setState()、只触发一次render()

不合并setState():setState()的回调函数

flushSync(callback):强制同步刷新DOM,挂起其他所有工作

React18前:只在合成事件(onClick)和钩子函数(usestate)有用

React18后:在Promise、setTimeout等原生事件中也有用

object.is():浅比较

比较更新前后的state,所以不能直接修改引用类型,应当新建+...

const [state, setState] = useState(initialState: S | (() => S)) 

setState(state: (prevState, props) => (return newState),callback?: () => void): void;

this.setState:对象与原状态进行浅合并

setState不会合并之前的值:...

推荐用法 ()=>{return },this.state. 

useReducer:复杂state

const [state, dispatch] = useReducer(reducer, initialArg, init?)

dispatch  action

手动调用reducer

受控组件和非受控组件

非受控组件:DOM操作

受控组件:state维护

执行过程:当前组件state队列、react批量更新状态

定义:组件私有属性,异步更新的响应式数据

你真的理解setState吗? - 掘金

组件私有属性,值是对象,当 state 发生变化时会触发 React 组件重新渲染视图

响应式数据

props 同步更新,state 异步更新的,最好不要用prevState值去计算下一个 state 的值

加入队列,只影响从 下一个 渲染开始返回的 this.state

setState其实本身执行的过程和代码都是同步的

自动批处理:合并多个setState()、只触发一次render()

同一时机多次调用`setState()`方法的一种处理机制,有助于减少在状态更改时发生的重新渲染次数。

handleClick = () => {  
    this.setState({
        msg: 'hi'
    });
    this.setState({
        count: 1
    });
}

//虽然调用了两次`setState()`方法,但是只会触发一次`render()`方法的重新执行。

不合并setState():setState()的回调函数

//同样的数据修改只会修改一次,可利用`setState()`的回调函数写法来保证每一次都能触发。
//因为不能比较函数体是否一致
this.setState((state)=> ({count: state.count + 1}));
this.setState((state)=> ({count: state.count + 1}));

flushSync(callback):强制同步刷新DOM,挂起其他所有工作

let { useState } = React;
let { flushSync } = ReactDOM;
let Welcome = (props) => {
    const [count, setCount] = useState(0);
    const handleClick = () => {
        flushSync(()=>{
          setCount(count + 1)
        })
    }
    return (
        <div>
            <button onClick={handleClick}>点击</button>
            <div>hello world, { count }, { msg }</div>
        </div>
    );
}

React18前:只在合成事件(onClick)和钩子函数(usestate)有用

原生自带的事件监听 addEventListener ,或者也可以用原生js、jq直接 document.querySelector().onclick 这种绑定事件的形式都属于原生事件。

react为了解决跨平台,兼容性问题,自己封装了一套事件机制,代理了原生的事件。

像在jsx中常见的onClickonChange这些都是合成事件

 
function  App ()  { 
  const  [ count ,  setCount ]  =  useState ( 0 ) ; 
  const  [ flag ,  setFlag ]  =  useState ( false ) ;
 
  function handleClick ( )  { 
    setCount ( c  =>  c  +  1 ) ;  // 还没有重新渲染
    setFlag ( f  =>  ! f ) ;  // 还没有重新渲染
    // React 只会在最后重新渲染一次(这是批处理!)
  }
 
  return  ( 
    < div > 
      < button  onClick = { handleClick } > Next < / button > 
      < h1  style = { {  color : flag ? "blue" : "black"  } } > { count } < / h1 > 
    < / div > 
  ) ; 
}

React18后:在Promise、setTimeout等原生事件中也有用

handleClick = () => {  
    setTimeout(()=>{
        this.setState({
            msg: 'hi'
        });
        this.setState({
            count: 1
        });
    }, 2000)
}

//上面代码在React18之前的版本中,将会触发两次`render()`方法。
//默认是自动批处理的,当然也可以改成不是自动批处理的方式,通过`ReactDOM.flushSync`这个方法。
import { flushSync } from 'react-dom'; // Note: react-dom, not react
handleClick = () => {  
    ReactDOM.flushSync(()=>{
        this.setState({
            msg: 'hi'
        });
    })
    ReactDOM.flushSync(()=>{
        this.setState({
            count: 1
        });
    }) 
}

object.is():浅比较

比较更新前后的state,所以不能直接修改引用类型,应当新建+...

function reducer(state, action) {
  switch (action.type) {
    case 'incremented_age': {
      // ✅ 修复:创建一个新的对象
      return {
        ...state,
        age: state.age + 1
      };
    }
    case 'changed_name': {
      // ✅ 修复:创建一个新的对象
      return {
        ...state,
        name: action.nextName
      };
    }
    // ...
  }
}

const [state, setState] = useState(initialState: S | (() => S)) 

setState(state: (prevState, props) => (return newState),callback?: () => void): void;

this.setState:对象与原状态进行浅合并

this.setState({count:1}) 
//箭头函数:相当于直接使用返回对象 {count: 1}
this.setState(() => ({count: 1}));

合并(shallow merge)是指将两个对象进行合并,如果两个对象有相同的属性名,则后一个对象的属性值会覆盖前一个对象的属性值

但是,如果属性值是一个对象,则不会进行递归合并,而是直接用后一个对象的属性值替换前一个对象的属性值。

import FormRender, { useForm } from 'form-render';
const form = useForm();
//formData 为form.getValues()的非空值
setData({ ...data, ...formData });

setState不会合并之前的值:...

const [info, setInfo] = useState({
    username: 'xiaoming',
    age: 20
})
setInfo({
    ...info,
    username: 'xiaoqiang'
})

推荐用法 ()=>{return },this.state. 

setState时使用函数返回新state值,(避免多个 setState 同时调用导致的状态更新冲突问题)

回调函数中获取最新更新后的state

handleClick = () => {
    this.setState(() => { return { board: this.jsonMsg[0].data.board_data[Number(key)] };}, 
() => {
      console.log('Updated count:', this.state.count);
    });
  };

useReducer:复杂state

const [state, dispatch] = useReducer(reducer, initialArg, init?)

let { useReducer } = React;
let loginState = {//体现整体关联性与统一性**
    isLogin: true,
    isLogout: false
}
let loginReducer = (state, action) => {
    switch(action.type){
        case 'login':
            return { isLogin: true, isLogout: false }
        case 'logout':
            return { isLogin: false, isLogout: true }
        default: 
           throw Error('Unknown action: ' + action.type);
    }
}
let Welcome = (props) => {  
    const [ state, loginDispatch ] = useReducer(loginReducer, loginState);
    const handleLogin = () => {
        loginDispatch({ type: 'login' });
    }
    const handleLogout = () => {
        loginDispatch({ type: 'logout' });
    }
    return (
        <div>
            { state.isLogin ? <button onClick={handleLogout}>退出</button> : <button onClick={handleLogin}>登录</button> }
        </div>
    );
}

dispatch  action

手动调用reducer

const action = { type: 'incremented_age' };

dispatch(action);

const nextState = reducer(state, action);

console.log(state); // { age: 42 }

console.log(nextState); // { age: 43 }

受控组件和非受控组件

受控和非控:对某个组件状态的掌控,它的值是否只能由用户设置,而不能通过代码控制

非受控组件:DOM操作


现用现取,官方建议尽量少用ref,用多了有一定的效率影响

handleSubmit = (event) => {
            event.preventDefault() //阻止表单提交
            const {username, password} = this//拿到的是form下的username, password结点
            alert(`你输入的用户名是:${username.value},你输入的密码是:${password.value}`)
        }
 
render() {
return (
<form onSubmit={this.handleSubmit}>
用户名:<input ref={c => this.username = c} type="text" name="username"/>
密码:<input ref={c => this.password = c} type="password" name="password"/>
<button>登录</button>


受控组件:state维护

    class Login extends React.Component {
 
//state最好要初始化状态
        state = {
            username: '', //用户名
            password: '' //密码
        }
 
//保存用户名到状态中
        saveUsername = (event) => {
this.setState({username: event.target.value})
        }
 
//保存密码到状态中
        savePassword = (event) => {
this.setState({password: event.target.value})
        }
 
//表单提交的回调
        handleSubmit = (event) => {
          event.preventDefault() //阻止表单提交
          const {username, password} = this.state//获得的是state下的username,password值
          alert(`你输入的用户名是:${username},你输入的密码是:${password}`)
        }

执行过程:当前组件state队列、react批量更新状态

当所有组件didmount后,父组件didmount,会将isBranchUpdate设置为false。这时将执行之前累积的setState,更新时会把每个组件的更新合并,每个组件只会触发一次更新的生命周期。

  • _pendingStateQueue当前组件等待执行更新的state队列
  • isBatchingUpdates:react用于标识当前是否处于批量更新状态,所有组件公用
  • dirtyComponent:当前所有处于待更新状态的组件队列

事务机制:数据库特有的术语,同步发生数据更新时,防止数据的不一致。

1.将setState传入的partialState参数存储在当前组件实例state暂存队列中。

2.判断当前React是否处于批量更新状态,如果是,将当前组件加入待更新的组件队列中。

3.如果未处于批量更新状态,将批量更新状态标识设置为true,用事务再次调用前一步方法,保证当前组件加入到了待更新组件队列中。

4.调用事务的waper方法,遍历待更新组件队列依次执行更新。

5.执行生命周期componentWillReceiveProps

6.将组件的state暂存队列中的state进行合并,获得最终要更新的state对象,并将队列置为空。

7.执行生命周期componentShouldUpdate,根据返回值判断是否要继续更新。

8.执行生命周期componentWillUpdate

9.执行真正的更新render

10.执行生命周期componentDidUpdate

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值