组件
创建组件的方式
创建组件有函数组件和类组件两种方式,React18 之后,全面使用函数组件,类组件会退出历史舞台
function Home(props) {
return <div className="home">Welcome to React~</div>;
}
const Home = () => {
return <div className="home">Welcome to React~</div>;
};
对组件的要求
- 组件名称必须以大写字母开头,否则 React 会将以小写字母开头的组件视为原生 DOM 标签
- 必须返回可以渲染的元素
- react 元素
- null
- 组件
- 可迭代的对象,包括数组、Set、Map 等
function App1() {
return null;
}
function App2() {
return [1, 2, 3];
}
// 如果直接返回对象,会报错:Uncaught Error: Objects are not valid as a React child
function App3() {
return { a: 1 };
}
那么是否可以说 React 组件不能返回对象?不能,因为可以返回一个迭代器
const obj = { a: 1 };
obj[Symbol.iterator] = function* () {
for (let prop in obj) {
yield [prop, obj[prop]];
}
};
function App() {
return obj;
}
组件重新渲染的条件
- 自身状态发生变化
- 父组件重新渲染
数据
所有 React 组件都必须像纯函数一样保护它们的props
不被更改
改变数据核心思想:先拷贝这个对象或数组,再改变这个拷贝后的值
更新对象:创建一个新的对象,通常使用展开运算符
const [person, setPerson] = useState({
name: 'Niki de Saint Phalle',
artwork: {
title: 'Blue Nana',
city: 'Hamburg',
image: 'https://i.imgur.com/Sd1AgUOm.jpg'
}
});
如果要更新 person.artwork.city 的值:
setPerson(prevPerson => ({
...prevPerson,
artwork: {
...prevPerson.artwork,
city: 'beijing'
}
}));
更新数组:
- 添加:
setList([...list, 666])
- 删除:通常使用 filter 方法生成一个不包含该元素的新数组
- 更新:
map()
Fragments
简单说就是避免向 DOM 中添加额外的节点。
假如有一个子组件<Columns />
const Columns = () => {
return (
<div>
<td>Hello</td>
<td>World</td>
</div>
);
};
有一个父组件使用了<Columns />
const Columns = () => {
return (
<table>
<tr>
<Columns />
</tr>
</table>
);
};
结果如下,在 tr 和 td 之间多了一个 div 节点,这样就导致了 html 是无效的
<table>
<tr>
<div>
<td>Hello</td>
<td>World</td>
</div>
</tr>
</table>
Fragments
就解决了这个问题
const Columns = () => {
return (
<React.Fragment>
<td>Hello</td>
<td>World</td>
</React.Fragment>
);
};
也可以使用一种短语法,像空标签一样 <></>
const Columns = () => {
return (
<>
<td>Hello</td>
<td>World</td>
</>
);
};
严格模式
在脚手架生成的 main.js 中,会发现使用了严格模式 StrictMode。启用了严格模式后, React 会在开发环境下调用渲染函数两次
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.jsx';
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
作用:
- 检查组件是否是纯函数
- 及早发现 useEffect 中的错误
- 警告过时的 API
受控组件和非受控组件
在对表单进行处理时,需要考虑:
- 非受控组件:由用户控制 value
- 受控组件:由代码控制 value
非受控组件:代码可以设置表单的初始值 defaultValue,能改变 value 的只有用户,代码通过 onChange 事件监听用户输入。
import { ChangeEvent, useState } from 'react';
function App() {
console.log('render');
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value);
};
return <input type="text" defaultValue={'hello'} onChange={handleChange} />;
}
export default App;
受控组件:由代码改变 value 的值。如下示例,input 的值是通过inputValue
控制的,在 onChange 事件中通过setInputValue
更新inputValue
的值,这样就实现了受控组件。
如果将setInputValue(e.target.value)
注释掉,在输入时,会发现控制台打印的是最新的值,但是页面上 input 是不能输入的。
import { ChangeEvent, useState } from 'react';
function App() {
const [inputValue, setInputValue] = useState('hello');
console.log('render');
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
console.log(e.target.value);
setInputValue(e.target.value);
};
return <input type="text" value={inputValue} onChange={handleChange} />;
}
export default App;
React 表单内置的受控组件的行为:
1、value + onChange
<input type="text" value={inputValue} onChange={handleChange} />