Files
rikako-note/react/react文档.md
2023-05-28 20:58:00 +08:00

10 KiB
Raw Blame History

快速入门

创建和嵌套组件

React应用由组件组成每个组件都是UI的一部分组件拥有自己的外观和逻辑。

组件是返回标签的js函数

function MyButton() {
  return (
    <button>I'm a button</button>
  );
}

上述已经声明了MyButton组件可以将该组件嵌套到另一个组件中

export default function MyApp() {
  return (
    <div>
      <h1>Welcome to my app</h1>
      <MyButton />
    </div>
  );
}

react组件必须以大写字母开头而html组件则是必须以小写字母开头可以通过组件开头字母的大小写来区分html组件和react组件

jsx编写标签

上述返回标签的语法被称为jsx大多数react项目都支持jsx。
jsx比html更加严格:

  1. 所有的标签都要有闭合标签(例如<br/>)
  2. 组件不能返回多个标签,只能返回一个标签,如果存在多个,必须将其包含到一个公共的父级<div><>
    function AboutPage() {
         return (
             <>
             <h1>About</h1>
             <p>Hello there.<br />How do you do?</p>
             </>
         );
     }
    

添加样式

React中可以在标签中添加className属性来添加样式其和html中的class工作方式相同

<img className="avatar" />
/* In your CSS */
.avatar {
  border-radius: 50%;
}

显示数据

在jsx中标签位于js中而可以在标签内通过{}来计算js表达式并将其填充到标签中

return (
  <h1>
    {user.name}
  </h1>
);

jsx还可以将表达式的值传递给标签属性通过{}传递表达式的值

return (
  <img
    className="avatar"
    src={user.imageUrl}
  />
);

同时可以通过js表达式来复制css

const user = {
  name: 'Hedy Lamarr',
  imageUrl: 'https://i.imgur.com/yXOvdOSs.jpg',
  imageSize: 90,
};

export default function Profile() {
  return (
    <>
      <h1>{user.name}</h1>
      <img
        className="avatar"
        src={user.imageUrl}
        alt={'Photo of ' + user.name}
        style={{
          width: user.imageSize,
          height: user.imageSize
        }}
      />
    </>
  );
}

条件渲染

在React中没有特殊语法编写条件故而可以在js中通过if条件引入jsx

let content;
if (isLoggedIn) {
  content = <AdminPanel />;
} else {
  content = <LoginForm />;
}
return (
  <div>
    {content}
  </div>
);

也可以使用如下方式:

<div>
  {isLoggedIn ? (
    <AdminPanel />
  ) : (
    <LoginForm />
  )}
</div>

渲染列表

可以通过如下方式将js数组渲染为列表

const products = [
  { title: 'Cabbage', id: 1 },
  { title: 'Garlic', id: 2 },
  { title: 'Apple', id: 3 },
];
const listItems = products.map(product =>
  <li key={product.id}>
    {product.title}
  </li>
);

return (
  <ul>{listItems}</ul>
);

上述示例中,<li>有一个key属性对于列表中的每个属性都应该传递给其一个字符串或数字的key用于在兄弟节点之间唯一标识该元素。

响应事件

可以在组件中声明事件处理函数来响应事件

function MyButton() {
  function handleClick() {
    alert('You clicked me!');
  }

  return (
    <button onClick={handleClick}>
      Click me
    </button>
  );
}

更新界面

如果希望组件维护状态信息可以通过导入useState来完成

import { useState } from 'react';

function MyButton() {
  const [count, setCount] = useState(0);

其中count记录当前的状态而setCount则是用于改变状态的函数可以为数组中变量取任何名称。

import { useState } from 'react';

export default function MyApp() {
  return (
    <div>
      <h1>Counters that update separately</h1>
      <MyButton />
      <MyButton />
    </div>
  );
}

function MyButton() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <button onClick={handleClick}>
      Clicked {count} times
    </button>
  );
}

使用hook

use开头的函数被称为hookuseState是react提供的一个内置hook。
hook比普通函数更为严格只能在组件顶层或其他hook的顶层调用hook。如果想要在条件或循环中使用hook请新建一个组件并在组件内部使用。

组件之间共享数据

如果想要将状态在多个组件之间共享,需要将状态提升存储到最近的公共父组件中

export default function MyApp() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <div>
      <h1>Counters that update together</h1>
      <MyButton count={count} onClick={handleClick} />
      <MyButton count={count} onClick={handleClick} />
    </div>
  );
}

function MyButton({ count, onClick }) {
  return (
    <button onClick={onClick}>
      Clicked {count} times
    </button>
  );
}

此时由MyApp传递给MyButton的值称之为prop

jsx展开传递props

如果父组件想要将接收到的props全部传递给子组件无需在子组件上列出props中全部属性可以使用...props来进行展开

function Profile(props) {
  return (
    <div className="card">
      <Avatar {...props} />
    </div>
  );
}

将jsx作为子组件传递

当采用如下形式时:

<Card>
  <Avatar />
</Card>

其中父组件接收到的prop中children将代表接受到的子组件内容

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>
  );
}

此时想要嵌套在父组件内部的子组件可以通过props.children来访问。

条件渲染

如果在某些条件下组件不想显示任何内容可以返回null

function Item({ name, isPacked }) {
  if (isPacked) {
    return null;
  }
  return <li className="item">{name}</li>;
}

组件的渲染和提交

组件渲染的原因

  • 组件的初次渲染
  • 组件(或先祖组件)的状态发生了变化

初次渲染

当引用启动时,会触发初次渲染

状态更新时重新渲染

当初次渲染完成后可以通过set函数来更新state并触发渲染。更新组件状态会将重新渲染加入到队列。

对于初次渲染React会调用根部组件的方法来进行渲染而对于状态更新触发的渲染react只会调用状态更新对应组件的函数

对于组件的渲染其过程是递归的。如果待渲染的组件中包含了子组件那么react会对子组件进行渲染对子组件中包含的孙组件也同样。渲染会对组件子树中所有的组件进行渲染。

渲染差异

React只会在渲染之间存在差异时才会更改React节点如一个节点如果渲染之间从父组件接受到了不同的props此时该组件才会发生重新渲染。

state快照

在渲染函数中state的快照是不可变的即使在调用setState函数将更新state后旧组件中获取的state值仍然没有变化。

// 即使调用setTitle此快照中的title值在setTitle之前和之后仍然没有变化
// 而新渲染的组件title初始值则为改变之后的值
function Title() {
  const [title,setTitle]=useState('Morning News');

  return (
    <div>
      <h1>{title}</h1>
      <button onClick={
        ()=>{
          alert(title);
          setTitle(title==='Morning News'?'Evening Newng News':'Morning News');
          alert(title);
        }
      }>Switch</button>
    </div>
  )
}

向setState中传入更新函数

react支持向setState中传入更新函数而不是更新后的值例如

setNumber(n => n + 1);
setNumber(n => n + 1);
setNumber(n => n + 1);

此时react会将上述三个函数以此加入到队列中并在下次渲染时遍历执行队列中的函数。

更新state对象

如果向useState中传入object那么在调用setState函数时则是应该传入一个新的对象而不是在原有state对象上进行更新。
应该将state对象看作是不可变的调用setState时应该创建一个state对象的副本并且修改副本对象后用副本对象的值去更新setState方法。

// 如果想要修改对象中的某个字段,可以使用如下方法
let oldObj = {
  name : 'kazusa',
  isMale: false,
};
let newObj = {
  // 展开对象中的属性
  ...odlObj,
  // 新设置某个属性,用于覆盖旧的值
  name:'mashiro',
}

修改嵌套对象中的属性

setPerson({
  ...person, // Copy other fields
  artwork: { // but replace the artwork
    ...person.artwork, // with the same one
    city: 'New Delhi' // but in New Delhi!
  }
});

Immer

如果要修改深层嵌套的state值可以引入Immer库来完成对象的拷贝工作。通过Immer库可以无需关心对象不可变而是直接以可变的语法来修改对象属性而Immer会实际帮忙处理对象不可便的工作。

// 通过该import代替react中useState
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数组

和object一样在react中数组应该也要是不可变的在更新state中的值时也应该再创建一个新的数组并用新的数组值设置