相关文章推荐
个性的红茶  ·  C语言 ...·  3 月前    · 
耍酷的大象  ·  spark ...·  4 月前    · 
闷骚的跑步鞋  ·  数据库PostgreSQL PG ...·  8 月前    · 
// 渲染 function render ( ) { ReactDOM . render ( < App / > , document . getElementById ( "root" ) ) ;

2. 实现效果

效果如下,每次点击加1,同时更新视图:
在这里插入图片描述

3.实现原理解析:

1 . 调用 useState() 方法, 传入一个参数(如上demo,假如为数字0), 所以,我需要有个 useState() 方法, 同时导出了 一个数组, 俩个元素, 第一个元素是要渲染的值, 第二个值是一个函数

// 模拟这个场景
function useState(value){
  function setState(){}
  return [value, setState];
 

2.useState 内部的 setState() 接收这个参数, 第一次初始化的时候,就是默认的值, 但之后的值,每次更新都会通过调用 setA() 函数(其实内部走的是setState 方法),同时传入一个新的值,覆盖掉老的值

// 模拟 setA 函数
function useState(value){
  // 次函数会接收一个新的值
  function setState(newValue){
   	value = newValue
   	// 调用更新视图
  return [value, setState];

4. 按以上的思路实现代码

function useState(value) {
   function setState(newValue) {
       value= newValue;
       render();
       console.log(newValue);
       // 调用更新视图
   return [value, setState];
//  App 组件
function App() {
   let [a, setA] = useState(0);
   // 改变a的值
   const changeA = () => {
       setA(a + 1);
       console.log("render");
   return (
           <div>{a}</div>
           <button onClick={changeA}> +</button>
// 渲染
function render() {
   ReactDOM.render(<App />, document.getElementById("root"));
render();

效果如下,发现并没有渲染视图, 同时打印发现,结果一直是1, 并没有根据上次的值进行累加
在这里插入图片描述

5. 分析没有渲染预想结果的原因

1.分析如下代码,首先第一个原因, 把每次传入进来的新的value的值 虽然进行了保存, 但每次调用setA() 传入的 a+1 值依赖的都是初始值 0,所以每次都会打印1

function useState(value) {
   function setState(newValue) {
       value= newValue;
       render();  // 调用更新视图
       console.log(newValue); // 永远是1
   return [value, setState];
 

2.改写, 首先定义一个全局变量,每次调用时, 先取一下上次最后保存的值, 如果有,就取出来上次的最后一个值,没有的话,就取默认值,也就是这里利用一下闭包

let lastValue;
function useState(value) {
	// 如果有,就取出来上次的最后一个值,没有的话,就取默认值
   lastValue = lastValue || value;
   function setState(newValue) {
       // 每次改变都保存更新的值
       lastValue = newValue;
       render();
       // 调用更新视图
   return [lastValue , setState];

6. 改写后实现结果

7. 当使用多个 useState时,数据不冲突的原理

先看上边的demo,自己实现的useState,调用多次,是有bug的, 更新a时, b也会同时更新

7.1 俩按钮的demo事件

import React from "react";
import ReactDOM from "react-dom";
let lastValue;
function useState(value) {
    lastValue = lastValue || value;
    function setState(newValue) {
        lastValue = newValue;
        render();
        // 调用更新视图
        console.log(lastValue);
    return [lastValue, setState];
//  App 组件
function App() {
    let [a, setA] = useState(0);
    let [b, setB] = useState(1);
    // 改变a的值
    const changeA = () => {
        setA(a + 1);
    // 改变b的值
    const changeB = () => {
        setB(a + 1);
    return (
            <div>{a}</div>
            <button onClick={changeA}> a+</button>
            <div>{b}</div>
            <button onClick={changeB}> b+</button>
// 渲染
function render() {
    ReactDOM.render(<App />, document.getElementById("root"));
render();

a按钮b按钮,都会同时更新,明明是俩变量,但是确同时更新
在这里插入图片描述

7.2 分析上边出现的原因,以及怎么解决保证具体是哪个state

  1. 怎么让同一个函数的调用,根据各自的参数, 产生对应的值,怎么来区分
  2. useState 会调用多次,每次怎么知道是谁的值,而lastState ,刚好保存的又是上次对应的自己的值
  3. 此时,我可以想到怎么区分, 一种是使用对象,或者map,但是用map,或者对象,但这里明显它俩不合适,不知道key怎么定义,还有就是数组,通过索引来找到具体的操作的是哪个 ,无法像下边这样写
  • 对象不合适
let index = 0;
let lastValue = {};
function useState(value) {
    // 明显的 lastValue  = {index:1}, 而我期望的是{"0":1}
    lastValue = lastValue[index] || { index: value };
    console.log(lastValue);
    let curIndex = index;
    function setState(newValue) {
        lastValue[curIndex] = newValue;
        render();
        // 调用更新视图
        console.log(lastValue);
    return [lastValue[curIndex++], setState];

7.3 利用数组的方式实现

采用数组索引的方式, 大致思路:

  1. 两个全局变量, 一个用于 将传入的值,存入到数组中,一个是默认的初始索引值0,
  2. 先取值,如果 数组 [index] 能取到值,就用 数组 [index], 取不到 就使用用户传入的值
  3. 调用 内部的 闭包函数 setState 时, 让 数组 [index] = newValue 新值
  4. 所以每次导出的值的索引都要加1, 使用后置++,以保证再下次再调用时,索引向上递增1,
  5. 同时,还要保存这个索引,因为它每次都会变

7.4 实现代码

let state = []; 
let index = 0;
function useState(value) {
    // 拿到本次索引的值
    state[index] = state[index] || value;
    // 保存本次的index
    let cutIndex = index;
    function setState(param) {
        state[cutIndex] = param;
        render();
    // 每调用一次, 索引再下次再调用时就+1
    return [state[index++], setState];
function App() {
    let [a, setA] = useState(0);
    let [b, setB] = useState(1);
    const changeA = () => setA(a + 1);
    const changeB = () => setB(b + 1);
    return (
            <div>{a}</div>
            <button onClick={changeA}> +</button>
            <div>{b}</div>
            <button onClick={changeB}> +</button>
function render() {
    ReactDOM.render(<App />, document.getElementById("root"));
render();

但你每次点击,索引索引再变, 但是视图并没有更新, 此时还需要每次render时都重置下索引

// ...略
function render() {
    index = 0;
    ReactDOM.render(<App />, document.getElementById("root"));
render();

8. 调用了自定义的setXXX 还是不更新视图

通常不更新视图,是因为你的数据原因,基本数据结构类型 不会 出现这种问题的, 引用数据类型根据你写法的不同是会出现这种情况的, 你每次调用自己的 setXXX 方法, 如果传入的是同一个引用的话, 是不会刷新视图,如下:

let obj = { num: 5 };
function App() {
    let [a, setA] = React.useState(obj);
    // 改变obj的a
    const changeA = () => {
        obj.num += 1;
        console.log(obj.num);
        setA(obj);
    return (
            <div>{a.num}</div>
            <button onClick={changeA}> +</button>
ReactDOM.render(<App />, document.getElementById("root"));

在这里插入图片描述
虽然数据再变, 但视图没有刷新, 所以对于引用数据类型,必须保证每次传入的不是同一个引用才会引起视图的更新,所以每次都是一个新的引用地址,对于复杂的数据结构,可以使用Object.create(), {},[],Object.assign(),等 处理一下

let obj = { num: 5 };
function App() {
    let [a, setA] = React.useState(obj);
    // 改变obj的a
    const changeA = () => {
        setA({ num: a.num + 1 });
    return (
            <div>{a.num}</div>
            <button onClick={changeA}> +</button>
ReactDOM.render(<App />, document.getElementById("root"));

在这里插入图片描述
不允许调用 forceUpdate 方法, 因为此方式只能类组件使用,就像 hooks 只能用在函数组件中一样

useEffect中监听state变化,存进ref中 import './App.css'; import React, { useState,useRef,useEffect} from 'react'; function App() { let [count,setCount]=useState(0) const myRef = 使用深拷贝:在更新 state 时,使用深拷贝将旧的 state 值复制给新的 state,这样一来,新的 state 值的地址与旧的 state 不同,就能够触发重新渲染了。在上述代码中,使用 JSON.parse(JSON.stringify()) 方法对旧的 state 进行深拷贝,并修改其 age 属性后作为新的 state 值,从而可以正常触发重新渲染。使用不可变数据类型:使用不可变的数据类型,如字符串、数字和布尔值,而不是引用类型的对象,这样就不会出现值被修改但地址相同的问题。 即使setPage了,拿到的page值依旧没有更新。 因为setState是异步的,所以在调用setState后,this.state不会立刻映射到新的值。 另外也不要指望设置timeout时间来调用,这是无用功(这坑我踩过)。 ( ̄_, ̄ ) 最好的办法,用hooks的useEffect方法,以page作为变量,触发副作用调用getList(),获取新一页的数据。 useEffect(() => { 1、在项目中遇到一个很奇怪的现象,使用useState来控制页面上组件的改变,发现并不是每次更改useState之后,页面都会跟着刷新。 2、研究发现useState更新的数据,是一个多层次的数据,react监听的时候,是浅层监听,所以不一定及时刷新页面。 3、首先想到的办法就是进行深拷贝,把需要更新的数据深拷贝一份,再使用useState 存储,就能实现每次都及时更新页面。 const onEnable = (row,index) => { const list = productList; list[index].enable = !list[index].enable; setProductList(list); console.log('o 一、useState 简介 返回一个有状态值和一个函数来更新它。在初始渲染期间,返回的状态(状态)与作为第一个参数(initialState)传递的值相同。setState 函数用于更新状态。它接受一个新的状态值,并排队等待重新渲染该组件。 在后续重新渲染期间,useState 返回的第一个值将始终是应用更新后的最新状态。 注意: React 保证 setState 函数身份是稳定的,并且在重新渲染时 <div className="selection" style={item.select == true?{backgroundColor:'rgba(0,230,155,.8)'}:{backgroundColor:'rgba(0,255,172,.1)'}} onClick={()=>selectReport(index,item.name)}></div> 这里我们有一个点击事件,让这个div变色。 const [reportChange,setrepor. ReactuseState hook更新数据时可能出现不同步的情况。这是因为 setState 方法是异步执行的,它会在当前代码执行完后才会更新组件的状态。如果您在 setState 方法中需要使用最新的状态值,可以使用 setState 方法的第二个参数(回调函数)来获取最新的状态值。 举个例子: const [count, setCount] = useState(0); func...