# 快速入门
## 创建和嵌套组件
React应用由组件组成,每个组件都是UI的一部分,组件拥有自己的外观和逻辑。
### 组件是返回标签的js函数
```js
function MyButton() {
return (
);
}
```
上述已经声明了MyButton组件,可以将该组件嵌套到另一个组件中
```js
export default function MyApp() {
return (
`或`<>`中
```js
function AboutPage() {
return (
<>
About
Hello there.
How do you do?
>
);
}
```
## 添加样式
React中,可以在标签中添加`className`属性来添加样式,其和html中的`class`工作方式相同
```html
![]()
```
```css
/* In your CSS */
.avatar {
border-radius: 50%;
}
```
## 显示数据
在jsx中,标签位于js中,而可以在标签内通过`{}`来计算js表达式并将其填充到标签中
```js
return (
{user.name}
);
```
jsx还可以将表达式的值传递给标签属性,通过`{}`传递表达式的值
```js
return (

);
```
同时,可以通过js表达式来复制css
```js
const user = {
name: 'Hedy Lamarr',
imageUrl: 'https://i.imgur.com/yXOvdOSs.jpg',
imageSize: 90,
};
export default function Profile() {
return (
<>
{user.name}

>
);
}
```
## 条件渲染
在React中,没有特殊语法编写条件,故而可以在js中通过if条件引入jsx:
```js
let content;
if (isLoggedIn) {
content =
;
} else {
content =
;
}
return (
{content}
);
```
也可以使用如下方式:
```js
```
## 渲染列表
可以通过如下方式将js数组渲染为列表
```js
const products = [
{ title: 'Cabbage', id: 1 },
{ title: 'Garlic', id: 2 },
{ title: 'Apple', id: 3 },
];
const listItems = products.map(product =>
{product.title}
);
return (
);
```
上述示例中,`
`有一个key属性,对于列表中的每个属性,都应该传递给其一个字符串或数字的key,用于在兄弟节点之间唯一标识该元素。
## 响应事件
可以在组件中声明事件处理函数来响应事件
```js
function MyButton() {
function handleClick() {
alert('You clicked me!');
}
return (
);
}
```
## 更新界面
如果希望组件维护状态信息,可以通过导入useState来完成
```js
import { useState } from 'react';
function MyButton() {
const [count, setCount] = useState(0);
```
其中,count记录当前的状态,而setCount则是用于改变状态的函数,可以为数组中变量取任何名称。
```js
import { useState } from 'react';
export default function MyApp() {
return (
Counters that update separately
);
}
function MyButton() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
);
}
```
## 使用hook
以`use`开头的函数被称为hook,useState是react提供的一个内置hook。
hook比普通函数更为严格,只能在组件顶层或其他hook的顶层调用hook。如果想要在条件或循环中使用hook,请新建一个组件并在组件内部使用。
## 组件之间共享数据
如果想要将状态在多个组件之间共享,需要将状态提升存储到最近的公共父组件中
```js
export default function MyApp() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
Counters that update together
);
}
function MyButton({ count, onClick }) {
return (
);
}
```
此时,由MyApp传递给MyButton的值称之为prop
## jsx展开传递props
如果父组件想要将接收到的props全部传递给子组件,无需在子组件上列出props中全部属性,可以使用`...props`来进行展开
```js
function Profile(props) {
return (
);
}
```
## 将jsx作为子组件传递
当采用如下形式时:
```js
```
其中,父组件接收到的prop中,children将代表接受到的子组件内容
```js
import Avatar from './Avatar.js';
function Card({ children }) {
return (
{children}
);
}
export default function Profile() {
return (
);
}
```
此时,想要嵌套在父组件内部的子组件可以通过props.children来访问。
## 条件渲染
如果在某些条件下,组件不想显示任何内容,可以返回null
```js
function Item({ name, isPacked }) {
if (isPacked) {
return null;
}
return {name};
}
```
## 组件的渲染和提交
### 组件渲染的原因
- 组件的初次渲染
- 组件(或先祖组件)的状态发生了变化
#### 初次渲染
当引用启动时,会触发初次渲染
#### 状态更新时重新渲染
当初次渲染完成后,可以通过set函数来更新state并触发渲染。更新组件状态会将重新渲染加入到队列。
对于初次渲染,React会调用根部组件的方法来进行渲染,而对于状态更新触发的渲染,react只会调用状态更新对应组件的函数
> 对于组件的渲染,其过程是递归的。如果待渲染的组件中包含了子组件,那么react会对子组件进行渲染,对子组件中包含的孙组件也同样。渲染会对组件子树中所有的组件进行渲染。
#### 渲染差异
React只会在渲染之间存在差异时才会更改React节点,如一个节点,如果渲染之间从父组件接受到了不同的props,此时该组件才会发生重新渲染。
## state快照
在渲染函数中,state的快照是不可变的,即使在调用setState函数将更新state后,旧组件中获取的state值仍然没有变化。
```js
// 即使调用setTitle,此快照中的title值在setTitle之前和之后仍然没有变化
// ,而新渲染的组件title初始值则为改变之后的值
function Title() {
const [title,setTitle]=useState('Morning News');
return (
{title}
)
}
```
## 向setState中传入更新函数
react支持向setState中传入更新函数而不是更新后的值,例如
```js
setNumber(n => n + 1);
setNumber(n => n + 1);
setNumber(n => n + 1);
```
此时react会将上述三个函数以此加入到队列中,并在下次渲染时遍历执行队列中的函数。
## 更新state对象
如果向useState中传入object,那么在调用setState函数时,则是应该传入一个新的对象,而不是在原有state对象上进行更新。
**应该将state对象看作是不可变的,调用setState时,应该创建一个state对象的副本,并且修改副本对象后用副本对象的值去更新setState方法。**
```js
// 如果想要修改对象中的某个字段,可以使用如下方法
let oldObj = {
name : 'kazusa',
isMale: false,
};
let newObj = {
// 展开对象中的属性
...odlObj,
// 新设置某个属性,用于覆盖旧的值
name:'mashiro',
}
```
### 修改嵌套对象中的属性
```js
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会实际帮忙处理对象不可便的工作。
```js
// 通过该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中的值时,也应该再创建一个新的数组,并用新的数组值设置