看react官网文档的学习笔记

155 阅读5分钟

react学习笔记

Hook

use 开头的函数被称为 Hook。Hook 比普通函数更为严格,只能在组件(或其他 Hook)的 顶层 调用 Hook。如果想在一个条件或循环中使用 useState,请提取一个新的组件并在组件内部使用它。

useState

useState是 React 提供的一个内置 Hook,用来创建响应式变量。

import { useState } from 'react';
const [count, setCount] = useState(0);

你将从 useState 中获得两样东西:当前的 state(count),以及用于更新它的函数(setCount)。

创建多个变量可以通过对象的键值对

    const [user, setUser] = useState({
        id: 1,
        name: '张三',
        auth: 'admin'
    });
     setUser({
            ...user,
            name: '李四'
        });

应该把state变量视为只读的

state初始值可以是任意js类型,修改时是创建新数据覆盖原有的数据,不应该直接更改,直接更改违背react设计理念。


    const [user, setUser] = useState({
        id: 1,
        name: '张三',
        auth: 'admin'
    });
    const changeUser = ()=>{
    //不应该直接修改
    user.name = '李四' // X
      setUser({
            ...user,
            name: '李四'
        });
    }

如果是一个多层嵌套的对象,可以用 Immer 库来创建对象

import { useImmer } from 'use-immer';
​
  const [person, updatePerson] = useImmer({
    name: 'Niki de Saint Phalle',
    artwork: {
      title: 'Blue Nana',
      city: 'Hamburg',
      image: 'https://i.imgur.com/Sd1AgUOm.jpg',
    }
  });
  
    function handleNameChange(e) {
    updatePerson(draft => {
      draft.name = e.target.value;
    });
  }

state变量的不变性,不会立即更新

state变量在程序运行时不会改变, 始终是初始值,或者是上次改变的值。 渲染后, 再次读取state才是上次改变的值, 在函数里改变了state,如果接下来立即读取他, 读取到的是程序开始运行时的值,不是改变的值,即时在异步函数中对state做处理,读取到的也是初始值。

原因:React 会等到事件处理函数中的 所有 代码都运行完毕再处理你的 state 更新。 这就是为什么重新渲染只会发生在所有这些 setNumber() 调用 之后 的原因。


import { useState } from 'react';
​
export default function Counter() {
  const [number, setNumber] = useState(0);
  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(number + 5);//理解为:setNumber(0 + 5)
        setTimeout(() => {
          alert(number); //这里读取到的是初始值 0 不会是5,理解为 alert(0);
        }, 1000);
      }}>+5</button>
    </>
  )
}

如果想在函数执行期间修改state的值,可以用 更新函数 来改变。


import { useState } from 'react';
​
export default function Counter() {
  const [number, setNumber] = useState(0);
​
  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(n => n + 1);
        setNumber(n => n + 1);
        setNumber(n => n + 1);
      }}>+3</button>
    </>
  )
}

React 使用单向数据流,useState定义的数据通过组件层级结构从父组件传递数据至子组件。

设计 state 应该被放置于哪里:

  1. 通常情况下,你可以直接放置 state 于它们共同的父组件。
  2. 你也可以将 state 放置于它们父组件上层的组件。
  3. 如果你找不到一个有意义拥有这个 state 的地方,单独创建一个新的组件去管理这个 state,并将它添加到它们父组件上层的某个地方。

3000 x 10

下跌3% 2 6000

  1. 26000 - 780 + 6000 = 31220 32000

  2. 32000- 960 + 6000 = 37040 38000

  3. 38000 - 1140 + 6000 =42860 44000

  4. 44000 - 1320 + 6000 = 48680 50000

    抛 止损

上涨2% 1 3000

  1. 26000 + 520 + 3000 = 29520

  2. 29000+ 520 + 580 + 3000 = 33100

  3. 31000 + 520 +580 +620 +3000 = 35720

组件

根组件 - 顶层组件 - 叶组件

React 组件是一段可以使用标签进行扩展的 JavaScript 函数,是构建用户界面(UI)的基础,组件的名称必须以大写字母开头,并返回JSX标签


export default function Profile() {
  return (
    <img
      src="https://i.imgur.com/MK3eW3Am.jpg"
      alt="Katherine Johnson"
    />
  )
}

纯函数定义组件

纯函数:保持组件的纯粹,只负责自己的任务,输入相同,输出也相同。React 便围绕着这个概念进行设计。React 假设你编写的所有组件都是纯函数。

使用纯函数的好处: 避免一些奇怪的bug,逻辑更清晰, 对于一些确定输入相同的纯函数,可以跳过渲染,提升性能。

props组件通信

不要在组件中定义组件,那会是应用程序变的非常卡顿。 应该在文件的最顶层定义各个组件,父子组件可以通过prop传递数据。

  • 如何向组件传递 props
  • 如何从组件读取 props
  • 如何为 props 指定默认值
  • 如何给组件传递 JSX
  • Props 如何随时间变化

props可以传递任何js值,包括对象,数组,函数。

父传子


//子组件 count = 1默认为1 没有传递count或者count={undefined}时生效。 
const Mybutton = ({count = 1,play}) =>{
    return <button onClick={play}>{count}</button>
}
​
//父组件
const Page = ()=>{
  return (
    <h3>html标题</h3>
    <Mybutton count={1} play=()=>{console.log(121)}></Mybutton>
  )
}

一个组件接收另一个组件作为参数,并渲染这个子组件.父组件将在名为 children 的 prop 中接收到该内容。


import Avatar from './Avatar.js';
​
function Card({ children }) {
  return (
    <div className="card">
      {children}
    </div>
  );
}
​
export default function Profile() {
  return (
    <Card>
      <Avatar
        size={100}
        person={{ 
          name: 'Katsuko Saruhashi',
          imageId: 'YfeOqp2'
        }}
      />
    </Card>
  );
}

一个组件接受JSX代码作为参数,并渲染JSX代码


const Card = ({children}) => {
    return (
        <div className="card">
            <div className="card-content">{children}</div>
        </div>
    );
};
​
export default function Profile() {
    return (
        <div>
            <Card>
                <h1>Photo</h1>
                <img className="avatar" src="https://i.imgur.com/OKS67lhm.jpg" alt="Aklilu Lemma" width={70} height={70} />
            </Card>
            <Card>
                <h1>About</h1>
                <p>Aklilu Lemma was a distinguished Ethiopian scientis.</p>
            </Card>
        </div>
    );
}

交互

响应事件

  • 必须传递事件处理函数,而非函数调用! onClick={handleClick} ,不是 onClick={handleClick()}。带括号是立即调用,会在渲染阶段触发事件。

1.阻止事件冒泡 e.stopPropagation()


  <button onClick={e => {
      e.stopPropagation();
      onClick();
    }}>
     点击按钮
    </button>

2.阻止浏览器默认行为 e.preventDefault()


export default function Signup() {
  return (
    <form onSubmit={e => {
      e.preventDefault();
      alert('提交表单!');
    }}>
      <input />
      <button>发送</button>
    </form>
  );
}

面试知识点

为什么多个JSX标签需要被一个父元素包裹?

JSX 虽然看起来很像 HTML,但在底层其实被转化为了 JavaScript 对象,你不能在一个函数中返回多个对象,除非用一个数组把他们包装起来。这就是为什么多个 JSX 标签必须要用一个父元素或者 Fragment(空标签) 来包裹。