Published on

描述UI和添加交互

Authors
  • avatar
    Name
    李丹秋
    Twitter

笔记

Thinking in React

Step1: 按照层级结构,划分页面

  • 程序角度:遵守单一职责原则,一个组件只做一件事情。
  • CSS角度:考虑css选择器的结构
  • 设计角度:考虑如何组织设计层级

Step2: 尽可能创建静态容器

自顶向下的去创建容器,底层组件应该只需要作为UI渲染组件,避免拥有自己的状态。

Step3: 找到UI状态的最小但完整的表示

  1. 如果一个组件的所有数据都来自于props,则不是有状态组件
  2. 如果一个数据会被频繁修改且不能通过其他有状态数据合成得到,则是有状态数据
  3. 如果每次渲染数据都需要保持不变,则不是有状态组件

Step4: 识别State应该存放在那里

  1. 识别哪些组件需要有状态
  2. 寻找这些组件的公共父节元素
  3. 决定状态应该存放在那里

Step4: 添加逆向数据流

为你的组件添加可以触发父级状态改变的函数。

事件交互

事件冒泡

export default function Toolbar() {
  return (
    <div className="Toolbar" onClick={() => {
      alert('You clicked on the toolbar!');
    }}>
      <button onClick={() => alert('Playing!')}>
        Play Movie
      </button>
      <button onClick={() => alert('Uploading!')}>
        Upload Image
      </button>
    </div>
  );
}

默认会执行事件冒泡,执行子元素click事件后,会继续出发父元素click事件, 可以添加e.stopPropagation来阻止冒泡

组件内存

import { useState } from 'react';
import { sculptureList } from './data.js';

export default function Gallery() {
  const [index, setIndex] = useState(0);

  function handleClick() {
    setIndex(index + 1);
  }

  let sculpture = sculptureList[index];
  return (
    <>
      <button onClick={handleClick}>
        Next
      </button>
      <h2>
        <i>{sculpture.name} </i>
        by {sculpture.artist}
      </h2>
      <h3>
        ({index + 1} of {sculptureList.length})
      </h3>
      <img
        src={sculpture.url}
        alt={sculpture.alt}
      />
      <p>
        {sculpture.description}
      </p>
    </>
  );
}

这里通过useState,使得index具备State,每次修改都会重新渲染页面,注意sculpture并没有使用useState,每次重新渲染,都会根据index获取最新的值,这也符合上面提到的能够通过现有state生成的数据,无需变成state

组件每一次重新渲染,useState都会返回两个值,第一个是被存储的state variable, 第二个是state setter function。当第二次渲染的时候,虽然执行的是初始值,但是react记得之前改变后的值。

Reader and Commit

  1. 触发渲染,分为首次渲染后再次渲染
  2. diff 算法
  3. appendChild
  4. Broswer Render

State as a Snapshot

当我们去更新state的时候,才会执行渲染。 当React重新渲染组件的时候,会执行以下操作

  1. React 再一次执行函数组件
  2. 函数组件返回一个新的jsx快照,函数执行过程中所有的props、事件、本地变量,都是用的当前渲染时候的state值被计算出来的。
  3. React更新视图去匹配函数执行的结果
import { useState } from 'react';

export default function Form() {
  const [to, setTo] = useState('Alice');
  const [message, setMessage] = useState('Hello');

  function handleSubmit(e) {
    e.preventDefault();
    setTimeout(() => {
      alert(`You said ${message} to ${to}`);
    }, 5000);
  }

  return (
    <form onSubmit={handleSubmit}>
      <label>
        To:{' '}
        <select
          value={to}
          onChange={e => setTo(e.target.value)}>
          <option value="Alice">Alice</option>
          <option value="Bob">Bob</option>
        </select>
      </label>
      <textarea
        placeholder="Message"
        value={message}
        onChange={e => setMessage(e.target.value)}
      />
      <button type="submit">Send</button>
    </form>
  );
}

以上示例,当我点击了发送之后,然后再改变下拉框的值,弹出的结果还是点击那一瞬间的值。

Queueing a Seriew of State Updates

React只有当事件函数执行完成之后,才会触发渲染,但是多个事件函数之间的独立的,会独立执行多次渲染。

  setNumber(number + 5);
  setNumber(n => n + 1);
  setNumber(42);
queued updatenreturns
replace with 50 (unused)5
n => n + 155 + 1 = 6
“replace with 42”6 (unused)42

Update Objects/Array in State

建议使用immer这个三方库去存储对象