React冷门但很好用的知识点

介绍

最近在重读React官方文档,盘点一些不经常使用但有用的知识点。若是有啥说的很差的地方,欢迎指正!html

推荐个翻译的不错的React文档react

getSnapshotBeforeUpdate()

getSnapshotBeforeUpdate()为React生命周期函数, 在render()以前调用。它使得组件能在发生更改以前从 DOM 中捕获一些信息(例如,滚动位置)。今生命周期的任何返回值将做为参数传递给 componentDidUpdate()dom

处理聊天滚动的示例:ide

class ScrollingList extends React.Component {
  constructor(props) {
    super(props);
    this.listRef = React.createRef();
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    // 咱们是否在 list 中添加新的 items ?
    // 捕获滚动位置以便咱们稍后调整滚动位置。
    if (prevProps.list.length < this.props.list.length) {
      const list = this.listRef.current;
      return list.scrollHeight - list.scrollTop;
    }
    return null;
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    // 若是咱们 snapshot 有值,说明咱们刚刚添加了新的 items,
    // 调整滚动位置使得这些新 items 不会将旧的 items 推出视图。
    //(这里的 snapshot 是 getSnapshotBeforeUpdate 的返回值)
    if (snapshot !== null) {
      const list = this.listRef.current;
      list.scrollTop = list.scrollHeight - snapshot;
    }
  }

  render() {
    return (
      <div ref={this.listRef}>{/* ...contents... */}</div>
    );
  }
}
复制代码

forceUpdate()

默认状况下,当组件的 stateprops 发生变化时,组件将从新渲染。若是 render() 方法依赖于其余数据,则能够调用 forceUpdate() 强制让组件从新渲染。函数

调用 forceUpdate() 将导致组件调用 render() 方法,此操做会跳过该组件的 shouldComponentUpdate()。但其子组件会触发正常的生命周期方法,包括 shouldComponentUpdate() 方法。若是标记发生变化,React 仍将只更新 DOM。this

简单来讲forceUpdate()方法能够强制更新组件,因此不建议使用!spa

key

key顾名思义为组件的标识,当key改变时组件会从新注册!但咱们使用最多的仍是遍历!翻译

const todos = [1, 2, 3, 4, 5];
const todoItems = todos.map((todo, index) =>
  // Only do this if items have no stable IDs
  <li key={index}>
    {todo.text}
  </li>
);
复制代码

其实用index做为Key有问题,能够看这篇博客code

状态提高

官方解释:一般,多个组件须要反映相同的变化数据,这时咱们建议将共享状态提高到最近的共同父组件中去。component

简单来讲,就是两个子组件都使用父组件的一个参数,经过props把改参数传入子组件,并把改变该参数的方法一并传入子组件,经过调用该方法改变父组件共用的参数!

举个例子🌰

如今有ComponentA以及ComponentB,他们各有一个输入框,当我在其中一个输入框输入英文单词时,在另外一个组件展现转换后的大小写。

class ComponentA extends React.Component {
  // 当input输入时,在另外一个组件显示大的大小写转换
  handleChange(e) => {
    const { onChangeWordUpper } = this.props;
    onChangeWordUpper(e.target.value);
  };

  render() {
    const { word } = this.props;
    return (
      <h2>ComponentA enter: {word}</h2>
      <input onChange={this.handleChange} />
    );
  }
}
复制代码
class ComponentB extends React.Component {
  // 当input输入时,在另外一个组件显示大的大小写转换
  handleChange(e) => {
    const { onChangeWordLower } = this.props;
    onChangeWordLower(e.target.value);
  };

  render() {
    const { word } = this.props;
    return (
      <h2>ComponentB enter: {word}</h2>
      <input onChange={this.handleChange} />
    );
  }
}
复制代码

父组件componentFather

class ComponentFather extends React.Component {
  state = {
    word: '';
  }

  // 转换为大写
  onChangeWordUpper(value) => {
    const word = value.toUpperCase()
    this.setState({ word });
  }
  
  // 转换为小写
  onChangeWordLower(value) => {
    const word = value.toLowerCase()()
    this.setState({ word });
  }

  render() {
    const { word } = this.state;
    const componentAProps = {
      word,
      onChangeWordUpper: this.onChangeWordUpper,
    };
    const componentBProps = {
      word,
      onChangeWordUpper: this.onChangeWordLower,
    };
    return (
      <ComponentA {...componentAProps} />
      <ComponentB {...componentBProps} />
    );
  }
}
复制代码

Fragments

Fragments你们应该都很熟悉!

React 中的一个常见模式是一个组件返回多个元素。Fragments 容许你将子列表分组,而无需向 DOM 添加额外节点

<React.Fragment>
  <div>somethings</div>
  <div>somethings</div>
</React.Fragment>
复制代码

能够简写为

<>
  <div>somethings</div>
  <div>somethings</div>
</>
复制代码

children prop

当一个组件没法知晓子组件的内容时,children prop就颇有用。

它能够把子组件整个传递下去!

举个例子🌰

// 父组件
function Header(props) {
  return (
    <div> {props.children} </div>
  );
}

function Page() {
  return (
    <Header> <h1>children - 1</h1> <h1>children - 2</h1> </Header>
  );
}
复制代码

这样Header组件接受的children就是两个h1标签

Context

Context应该是很经常使用的Api,防止有人没使用过并巩固一下个人知识点,仍是写一下。

举个例子🌰

import React, { createContext } from "react";
import ReactDOM from "react-dom";

const ThemeContext = createContext("red");

class App extends React.Component {
  state = {
    color: "green"
  };

  handleChangeColor = () => {
    if(this.state.color === 'green') {
      this.setState({ color: "red" });
    } else {
      this.setState({ color: "green" });
    }
  };

  render() {
    // 使用一个 Provider 来将当前的 color 传递给如下的组件树。
    // 不管多深,任何组件都能读取这个值。
    // 在这个例子中,咱们将 “dark” 做为当前的值传递下去。
    return (
      <ThemeContext.Provider value={this.state.color}>
        <Toolbar />
        <br />
        <button onClick={this.handleChangeColor}>changeColor</button>
      </ThemeContext.Provider>
    );
  }
}

// 中间的组件不再必指明往下传递 color 了。
function Toolbar(props) {
  return <ThemedSpan />;
}

class ThemedSpan extends React.Component {
  // 指定 contextType 读取当前的 color context。
  // React 会往上找到最近的 color Provider,而后使用它的值。
  // 在这个例子中,当前的 color 值为 “green”。
  static contextType = ThemeContext;
  render() {
    return <Span color={this.context} />;
  }
}

class Span extends React.Component {
  static contextType = ThemeContext;
  render() {
    return (
      <span style={{ backgroundColor: this.props.color }}>{this.context}</span>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("container"));

复制代码

注意事项:Provider的value我放在了state里。这样当value更新时不会致使其余组件渲染。

假设,value传入的是一个对象。

// bad
const obj = {a: 1};
<ThemeContext.Provider value={obj}> <Toolbar /> </ThemeContext.Provider> 复制代码

当obj改变,但obj.a 仍是 1,实际上,a的value并无改变,可是obj !== obj。

可是this.state.obj === this.state.obj

// good
<ThemeContext.Provider value={this.state.obj}>
  <Toolbar /> </ThemeContext.Provider> 复制代码

codesandbox地址

未完待续

相关文章
相关标签/搜索