React复习02-其他常用api

290 阅读4分钟

React常用的api

PureComponent

React.PureComponentReact.Component 很相似。两者的区别在于 React.Component 并未实现 shouldComponentUpdate(),而 React.PureComponent 中以浅层对比 prop 和 state 的方式来实现了该函数。

首先我们来看个例子

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

class App extends Component {
  state = {
    count: [0, 1],
    len: 2,
  };
  render() {
    const { count, len } = this.state;
    return (
      <>
        <button
          onClick={() => {
            this.setState({ count: [...count, len + 1], len: len + 1 });
          }}
        >
          +
        </button>
        {count.map((item, index) => (
          <Child data={item} key={index} />
        ))}
      </>
    );
  }
}

class Child extends Component {
  render() {
    const { data } = this.props;
    console.log(data);
    return <div>{data}</div>;
  }
}

export default App;

在这个例子中,我们只对count进行修改,看下另一个组件会发生什么

可以看到我们对count进行了修改,但是子组件全部重新渲染了,这并不是我们想要的。这时候我们可以使用PureComponent进行改写,只对新增的<Child/>进行重新渲染

class Child extends PureComponent {
  render() {
    const { data } = this.props;
    console.log(data);
    return <div>{data}</div>;
  }
}

可以看到,只有count变化了的组件才重新渲染,这无疑提高了性能。
当然我们也可以用shouldComponentUpdate进行手动优化。

class Child extends Component {
  shouldComponentUpdate(prevProps, prevState) {
  //  return true时,执行更新,return false不执行更新
    return prevProps === this.props;
  }

  render() {
    const { data } = this.props;
    console.log(data);
    return <div>{data}</div>;
  }
}

Ref

下面是几个适合使用 refs 的情况:

  • 管理焦点,文本选择或媒体播放。
  • 触发强制动画。
  • 集成第三方 DOM 库。 Refs 提供了一种方式,允许我们访问DOM 节点或在 render 方法中创建的React 元素

ref 的值根据节点的类型而有所不同:

  • 当 ref 属性用于 HTML 元素时,构造函数中使用 React.createRef() 创建的 ref 接收底层 DOM 元素作为其 current 属性。
  • 当 ref 属性用于自定义 class 组件时,ref 对象接收组件的挂载实例作为其current属性。
  • 你不能在函数组件上使用 ref 属性,因为他们没有实例

Ref用于html元素

接下来举个例子,通过双击p标签展示input标签并自动聚焦

import React, { Component } from "react";

class App extends Component {
  state = {
    msg: "hello world",
    textInput: React.createRef(),
    showInput: false,
  };

  focusTextInput = () => {
  	//	更新完showInput之后再执行聚焦,因为setState并非同步的
    this.setState({ showInput: true }, () => {
      this.state.textInput.current.focus();
    });
    console.log(this.state.textInput.current);
  };

  render() {
    const { msg, textInput, showInput } = this.state;
    const display = showInput ? "block" : "none";
    return (
      <>
      	{/* 双击后显示输入框 */}
        <p onDoubleClick={this.focusTextInput}>{msg}</p>  
        
        {/* 失去焦点后不显示输入框 */}
        <input
          type="text"
          ref={textInput}
          value={msg}
          style={{ display: display }}
          onChange={(e) => {
            this.setState({ msg: e.target.value });
          }}
          onBlur={() => {
            this.setState({ showInput: false });
          }}
        />
      </>
    );
  }
}

export default App;

Result:


Ref用于自定义class组件

import React, { Component } from "react";

class App extends Component {
  state = {
    textInput: React.createRef(),
  };

  componentDidMount() {
    console.log(this.state.textInput.current);
  }

  render() {
    const { textInput } = this.state;
    return (
      <>
        <Child ref={textInput} />
      </>
    );
  }
}

class Child extends Component {
  render() {
    return (
      <>
        <p>I m Child</p>
      </>
    );
  }
}

export default App;

Result:

回调Ref

React 也支持另一种设置 refs 的方式,称为“回调 refs”。它能助你更精细地控制何时 refs 被设置和解除。

不同于传递 createRef() 创建的 ref 属性,你会传递一个函数。这个函数中接受 React 组件实例HTML DOM 元素作为参数,以使它们能在其他地方被存储和访问。

接下来的例子,通过点击focus按钮,可以聚焦到input输入框

import React, { Component } from "react";

class App extends Component {
  selectEl = null;

  render() {
    return (
      <>
        <button
          onClick={() => {
            this.selectEl && this.selectEl.focus();
          }}
        >
          focus
        </button>
        <input
          ref={(node) => {
            this.selectEl = node;
            console.log(node);
          }}
        />
      </>
    );
  }
}

export default App;

Result:

Children

每个组件都可以获取到 props.children。它包含组件的开始标签和结束标签之间的内容

import React, { Component } from "react";

class App extends Component {
  selectEl = null;

  render() {
    return (
      <>
        <div>我是App</div>
        <Child>
          <div>我是传过去的1</div>
        </Child>
      </>
    );
  }
}

function Child(props) {
  console.log(props);
  return (
    <>
      <div>我是Child</div>
      {props.children}
    </>
  );
}

export default App;

Result:

如果我们传的内容是多个节点呢?

import React, { Component } from "react";

class App extends Component {
  selectEl = null;

  render() {
    return (
      <>
        <div>我是App</div>
        <Child>
          <div>我是传过去的1</div>
          <div>我是传过去的2</div>
          <div>我是传过去的3</div>
        </Child>
      </>
    );
  }
}

function Child(props) {
  console.log(props);
  return (
    <>
      <div>我是Child</div>
      {props.children}
    </>
  );
}

export default App;

dangerouslySetInnerHTML

有时候我们有这么个需求,直接把一段html代码给显示出来

import React, { Component } from "react";

class App extends Component {
  state = {
    data: `<h1>Hello, world</h1>`,
  };

  render() {
    const { data } = this.state;
    return (
      <>
        <div>我是App</div>
        {data}
      </>
    );
  }
}

export default App;

可以看到,react并没有把data当成html来解析。如果换成原生的js的话,我们可以直接设置el.innerHTML=data。在React中,也提供了类似的api,dangerouslySetInnerHTML

dangerouslySetInnerHTML 是 React 为浏览器 DOM 提供 innerHTML 的替换方案。通常来讲,使用代码直接设置 HTML 存在风险,因为很容易无意中使用户暴露于跨站脚本(XSS)的攻击。因此,你可以直接在 React 中设置 HTML,但当你想设置 dangerouslySetInnerHTML 时,需要向其传递包含 key 为 __html 的对象,以此来警示你.

import React, { Component } from "react";

class App extends Component {
  state = {
    data: `<h1>Hello, world</h1>`,
  };

  render() {
    const { data } = this.state;
    return (
      <>
        <div>我是App</div>
        <div dangerouslySetInnerHTML={{ __html: data }}></div>
      </>
    );
  }
}

export default App;