State,Ref,PureComponent的使用

1. setState的使用


import React, { Component } from "react";
import styled, { ThemeProvider } from "styled-components";

function Child(props) {
  return <p>{props.name}</p>;
}

const TTest = styled.p`
  font-size: ${(props) => props.theme.fontSize};
`;

class StateCombine extends Component {
  state = {
    name: "666",
    age: 80,
    count: 1,
  };

  changeName() {
    this.setState({
      name: "888",
    });
    // state的数据合并的原理就是下面的浅复制
    const result = Object.assign({}, { name: "666", age: 80 }, { name: "888" });
    console.log(result);
  }

  changeCount() {
    this.setState({
      count: this.state.count + 1,
    });
    this.setState({
      count: this.state.count + 1,
    });
    this.setState({
      count: this.state.count + 1,
    });
    // 原理也是 Object.assign, 后面会覆盖前面的
    const result = Object.assign({}, { count: 1 }, { count: 2 });
    console.log(result);
  }

  changeCount2() {
    // setState的第一个参数是函数的时候,会执行函数(和对象不一样),用call方法
    this.setState((prevState, prop) => ({ count: prevState.count + 1 }));
    this.setState((prevState, prop) => ({ count: prevState.count + 1 }));
    this.setState((prevState, prop) => ({ count: prevState.count + 1 }));
  }

  render() {
    const { name, age, count } = this.state;
    return (
      <ThemeProvider theme={{ fontSize: "30px" }}>
        <h3>state的数据合并</h3>
        <hr />
        <TTest>{name}</TTest>
        <p>{age}</p>
        <button onClick={() => this.changeName()}>数据的合并改变</button>
        <h3>setState的合并</h3>
        <hr />
        <p>{count}</p>
        <button onClick={() => this.changeCount()}>setState的合并改变</button>
        <h3>合并时进行累加</h3>
        <hr />
        <button onClick={() => this.changeCount2()}>setState的合并累加</button>
      </ThemeProvider>
    );
  }
}

export class TState extends Component {
  state = {
    name: "张三",
  };

  componentDidUpdate() {
    console.log(this.state.name, "componentDidUpdate");
  }

  /*
  为什么 setState 是异步的
  1. 可以显著提升性能  每次setState都更新,意味着render会频繁调用,界面重新渲染,效率很低,
  所以采用的是批量更新
  2. 同步更新了state后,render函数还没有执行,state和props就会出现不一致的情况
  */
  changeName = () => {
    this.setState(
      {
        name: "苏里斯",
      },
      () => {
        // 如果要获取最新的name,可以在这里获取,或者在componentDidUpdate获取,这里是在componentDidUpdate后面执行的
        console.log(this.state.name, "callback");
      }
    );
    // 这边没有改变,所以setState可以说是异步的(同步有几种特殊情况)
    console.log(this.state.name); // 张三
  };

  // setState同步的情况1
  changeNameSync = () => {
    setTimeout(() => {
      this.setState({
        name: "同步演示",
      });
      console.log(this.state.name, "同步"); // 这个会在componentDidUpdate后面执行
    }, 0);
  };

  componentDidMount() {
    // setState同步的情况2 dom操作里面
    document
      .getElementById("dom")
      .addEventListener("click", this.domHandler.bind(this));
  }

  domHandler() {
    this.setState({
      name: "同步演示",
    });
    console.log(this.state.name, "同步dom"); // 这个会在componentDidUpdate后面执行
  }

  render() {
    const { name } = this.state;
    return (
      <div>
        <h3>setState为啥是异步的,即时获取state的方式</h3>
        <hr />
        <p>{name}</p>
        <Child name={name}></Child>
        <p>
          <button onClick={this.changeName}>改变名字</button>
        </p>
        <h3>setState同步的情况</h3>
        <hr />
        <p>
          <button onClick={this.changeNameSync}>改变名字同步</button>
        </p>
        <p>
          <button id="dom">改变名字 DOM方式</button>
        </p>

        <StateCombine></StateCombine>
      </div>
    );
  }
}

export default TState;

2.Ref的使用

import React, { Component, createRef, forwardRef } from "react";

// function FChild() {
//   return <p>这是个函数组件</p>;
// }

// 传递ref
const Fr = forwardRef((props, ref) => {
  return <p ref={ref}>这是个函数组件</p>;
});

class CChild extends Component {
  render() {
    return <p>这是个class组件</p>;
  }
  say() {
    console.log("ref调用方法");
  }
}

// ref直接用字符串的方式不推荐,推荐使用createRef,对象的方式
export class TRef extends Component {
  cref = createRef();
  dref = createRef();
  fref1 = createRef();
  fref2 = createRef();
  render() {
    return (
      <div>
        <hr />
        <h3>Ref使用</h3>
        <p ref={this.dref}>dom元素ref测试</p>
        <p ref={(dom) => (this.fdom = dom)}>函数的模式获取dom</p>
        <CChild ref={this.cref} />
        {/* <FChild ref={this.fref1} /> */}
        <Fr ref={this.fref2} />
        <button onClick={() => this.printRef()}>打印ref</button>
      </div>
    );
  }
  printRef() {
    console.log(this.cref.current, this.dref.current);
    this.cref.current.say();
    console.log(this.fdom);

    console.log(this.fref1.current, this.fref2.current); // fref1是null,而且会警告,函数组件不能直接获取ref
  }
}

export default TRef;

3. 性能优化

import React, { Component, Fragment, PureComponent, memo } from "react";

// react更新流程
/*
** prop/state改变 => render函数重新执行 => 产生新DOM树 => 新旧DOM树diff算法,计算差异,进行更新 => 更新到真实DOM

** React diff比较
   1.同层节点之间比较,不会跨节点比较
   2.不同类型的节点,产生不同的树结构(类型不同,直接销毁包括子节点,不会再子节点比较,类型相同,保留DOM节点,然后比较改变的属性)
   3.可以通过key指定哪些节点在不同的渲染下保持稳定 (子节点递归)
   [比如数组最后插入,前面的没有变化,那前面的就不会销毁,最后一个添加即可]
   [如果是在第一个插入,那就会发现第一个发生差异,接着第二个,第三个...,都会发生差异,
    所以这种用key的话就能保持稳定,只需要更换位置即可,提高性能]
*/

// render调用

function Header() {
  console.log("Header render");
  return <h4>我是个头部</h4>;
}

// 函数式组件没有PureComponent,所以用memo
const MemoFooter = memo(() => {
  console.log("Footer render");
  return <h4>我是个尾部</h4>;
});

function Banner() {
  console.log("Banner render");
  return <h4>我是个Banner</h4>;
}

function List() {
  console.log("List render");
  return <h4>我是个List</h4>;
}

class Main extends PureComponent {
  render() {
    console.log("Main render");
    /* 用了PureComponent,父组件的count的变化不会导致Main的更新,Main没有render,那里面的子组件自然不会更新 */
    return (
      <Fragment>
        <Banner></Banner>
        <List />
      </Fragment>
    );
  }
}

export class TShouldUpdate extends Component {
  state = {
    count: 1,
  };

  changeCount = () => {
    this.setState({
      count: this.state.count + 1,
    });
  };

  render() {
    console.log("TShouldUpdate render");
    const { count } = this.state;
    return (
      <div>
        <h4>{count}</h4>
        {/* 父组件的render调用,会带动子组件的更新,触发子组件的render */}
        {/* 然而这个count跟下面的子组件没有关系,不应该触发子组件的更新 */}
        {/* 此时就应该进行shouldComponentUpdate的浅比较,优化性能,使用PureComponent(state或props发生改变) */}
        <button onClick={this.changeCount}>增加计数</button>
        <Header></Header>
        <Main></Main>
        <MemoFooter></MemoFooter>
      </div>
    );
  }
}

export default TShouldUpdate;

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值