前言
什么是 React?
React 是一个用于构建交互式 UI 的 JavaScript 库。它的主要特点包括:
组件化:UI 被拆分成独立的组件,便于复用和管理。声明式UI:使用 JSX(类 HTML 语法)编写代码,使 UI 状态更新更加直观。虚拟DOM:通过 Diffing 算法优化 DOM 更新,提高性能。
文中所有组件及方法为方便理解与概述做出了不同程度的整合,实际使用可根据具体业务拆分
一、创建React项目
1、使用CDN(不推荐)
xml
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>React 入门</title>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function App() {
return <h1>Hello, React!</h1>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
2、使用Vite并创建React项目(推荐)
npm create vite@latest my-react-app --template react
进入项目目录
cd my-react-app
安装依赖
npm install
启动开发服务器
npm run dev
二、react的基本概念
入口文件main.jsx解析
StrictMode
- StrictMode在开发环境下会:
-
- ✅ 检测废弃的生命周期方法使用
-
- ✅ 警告使用过时的字符串ref API
-
- ✅ 检测意外的副作用(组件会渲染两次)
-
- ✅ 检测过时的context API
-
- ✅ 检测不安全的副作用(未来并发特性相关)
注意:StrictMode只影响开发环境,生产构建时会自动移除
常见扩展模式:
-
- 添加路由:用
<BrowserRouter>包裹<App />
- 添加路由:用
-
- 添加状态管理:用
<Provider>包裹<App />
- 添加状态管理:用
-
- 添加主题:用
<ThemeProvider>包裹<App />
- 添加主题:用
-
- 添加错误边界:用
<ErrorBoundary>包裹<App />
- 添加错误边界:用
javascript
// StrictMode是React的开发工具,用于在开发阶段检测潜在问题
import { StrictMode } from 'react'
// createRoot用于创建React根容器,替代旧版的ReactDOM.render()
import { createRoot } from 'react-dom/client'
//导入全局CSS样式文件
import './index.css'
//导入主应用组件
import App from './App.jsx'
/**
* 创建React根节点并渲染应用
*
* 1. document.getElementById('root') - 获取HTML中的挂载点
* 2. createRoot() - 创建React 18的并发根容器
* 3. render() - 将React组件渲染到DOM中
*/
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>,
/**
* render()方法的第二个参数(可选):
* 可以在这里添加回调函数,在组件渲染或更新后执行
* 示例:.render(<App />, () => console.log('渲染完成'))
*/
)
1、JSX基本语法
- 组件
首字母必须大写 必须返回单一根元素,可以用<></>- 标签
必须闭合,如<img>必须为<img/> - 属性用 camelCase 方式,如
className(而不是class) - 样式相关可直接引入外部文件,例:
import './assets/style/index.css' - 此处为方便理解定义为
export default function App(){...},定义function App(){...}可直接书写export default App - window.location.replace("juejin.cn/user/840368...")
javascript
export default function App() {
return (
<>
<div className="title">标题文字</div>
</>
);
}
2、条件渲染
- 变量、动态类名、动态样式的使用
注意style的使用
css
function App() {
let titleText = '标题文字';
let num = 8;
let styleObj = { color: titleText?.length > 3 ? 'red' : 'blue' };
return (
<>
{/*变量的使用*/ }
<div>{ titleText }</div>
{/*动态类名的使用*/ }
<div className={ num > 5 ? 'big-class' : 'small-class' }>{ titleText }</div>
{/*动态样式的使用*/ }
{/*此处style后的第一层{}为包裹,第二层{}为样式对象,勿混淆*/ }
<div style={ { color: titleText?.length > 3 ? 'red' : 'blue' } }>{ titleText }</div>
{/*上面的style同下*/}
<div style={ styleObj }>{ titleText }</div>
</>
);
}
- 可以选择性地将一些
JSX赋值给变量,然后用大括号将其嵌入到其他JSX中 - 下列几种用法均可灵活组合。
- window.location.replace("juejin.cn/user/840368...")
javascript
function User({ userData }) {
let jsxAgeContent = userData.age >= 18 ? (<div>年龄:{ userData.age }</div>) : (<div>年龄:未成年</div>);
let jsxNameContent;
if(userData.name?.length > 2) {
jsxNameContent = (<div>{ `${ userData.name[0] }*${ userData.name[2] }` }</div>);
}
else {
jsxNameContent = (<div>{ `${ userData.name[0] }*` }</div>);
}
return (
<>
{ jsxNameContent }
{ jsxAgeContent }
<div>{ userData.isWork && <span>薪酬:¥ { userData.salary }/天</span> }</div>
</>
);
}
function Index() {
return (
<>
<User
userData={ { name: '张三三', age: 17, isWork: false, salary: null } }
/>
<User
userData={ { name: '李四', age: 21, isWork: true, salary: 888 } }
/>
<User
userData={ { name: '王五五', age: 36, isWork: true, salary: 8888 } }
/>
</>
);
}
export default Index;
3、列表渲染
- 使用
map重构、filter筛选等方法,渲染出一个组件列表 - 注意
唯一标识key,此处为方便演示使用index,实际开发要避免使用index作为key - 方法一、方法二为条件渲染的写法, 根据js逻辑可
灵活运用,不局限于map、filter、forEach等方法。 注意return用法,此处逻辑相对简单,已隐式省略。
javascript
export default function List() {
let listData = [
{ name: '张三三', age: 17, isWork: false, salary: null },
{ name: '李四', age: 21, isWork: true, salary: 888 },
{ name: '王五五', age: 36, isWork: true, salary: 8888 }
];
let listItems = listData.map((item, index) =>
<li key={ index }>{ item.name }</li>
);
// 方法一
// let filterData = listData.filter((item) => item.age > 18);
// let listItems = filterData.map((item, index) => <li key={ index }>{ item.name }</li>);
// 方法二
// let listItems = [];
// listData.forEach((item, index) => {
// if(item.age > 18) {
// listItems.push(<li key={ index }>{ item.name }</li>);
// }
// });
return <ul>{ listItems }</ul>;
}
4、事件处理
- 基础用法及事件传参
javascript
export default function List() {
let listData = [
{ name: '张三三', age: 17, isWork: false, salary: null },
{ name: '李四', age: 21, isWork: true, salary: 888 },
{ name: '王五五', age: 36, isWork: true, salary: 8888 }
];
function clickItem(item) {
alert(`点击了${ item.name }的信息`);
}
// 基础用法
// let listItems = listData.map((item, index) =>
// <li key={ index } item={item} onClick={ clickItem }>{ item.name }</li>
// );
// 事件传参
let listItems = listData.map((item, index) =>
<li key={ index } onClick={ () => clickItem(item) }>{ item.name }</li>
);
return <ul>{ listItems }</ul>;
}
5、组件通信Props(父传子)
props的作用与函数的参数相同,接收的永远是一个对象,是组件的唯一参数- 通过
参数=值的形式设置默认参数,在该参数不存在时使用默认值 在子组件标签上通过{...props},可以将所有属性扩展至子标签,不建议这么用,如有需求,可以通过jsx传递子组件
javascript
//写法一:
function User({ userData, title='个人信息' }) {
return (
<>
<h3>{ title }</h3>
<div>姓名:{ userData.name }</div>
<div>年龄:{ userData.age }</div>
</>
);
}
//写法二:
// function User(props) {
// let { title, userData } = props;
// // 等同于
// // let title=props.title;
// // let userData=props.userData;
// return (
// <>
// <h3>{ title }</h3>
// <div>姓名:{ userData.name }</div>
// <div>年龄:{ userData.age }</div>
// </>
// );
// }
//写法三:
// function User(props) {
// return (
// <>
// <h3>{ props.title }</h3>
// <div>姓名:{ props.userData.name }</div>
// <div>年龄:{ props.userData.age }</div>
// </>
// );
// }
function Index() {
return (
<>
<User
userData={ { name: '张三三', age: 18 } }
title="用户信息"
/>
<User
userData={ { name: '李四四', age: 21 } }
title="用户信息"
/>
<User
userData={ { name: '王五五', age: 36 } }
title="用户信息"
/>
</>
);
}
export default Index;
注意:嵌套JSX,将被视为父组件的children propProps是只读的时间快照,每次渲染都会收到新版本的props不能改变props,需要交互性时,可以设置state- window.location.replace("juejin.cn/user/840368...")
javascript
function User({ userData, title = '个人信息' }) {
return (
<>
<h3>{ title }</h3>
<div>姓名:{ userData.name }</div>
<div>年龄:{ userData.age }</div>
</>
);
}
function Index() {
return (
<>
<Content>
<User
userData={ { name: '张三三', age: 18 } }
title="用户信息"
/>
<User
userData={ { name: '李四四', age: 21 } }
/>
<User
userData={ { name: '王五五', age: 36 } }
/>
</Content>
</>
);
}
function Content({ children }) {
return (
<div className="user">
{ children }
</div>
);
}
export default Index;
6、状态管理(useState)
useState是一个 React Hook,它允许你向组件添加一个状态变量useState返回一个由两个值组成的数组:①初始化的state ②可以将state更新为不同的值并触发重新渲染- 只能在
组件的顶层调用,不能在循环或条件语句中调用 - set函数可以
直接传递新状态,也可以传递一个根据先前状态来计算新状态的函数 - set函数没有返回值
注意:调用set函数不会更新已经运行代码中的状态变量,如有同一变量的多次处理逻辑,应该处理完毕后再返回更新
基础用法
javascript
import { useState } from 'react';
export default function Index() {
const [age, setAge] = useState(18);
// 方法一
// function add() {
// setAge(agePrev => agePrev + 1);
// }
//
// return <>
// <button onClick={ add }>+1岁</button>
// 年龄:{ age }
// </>;
// 方法二
return <>
<button onClick={ ()=>setAge(agePrev => agePrev + 1) }>+1岁</button>
年龄:{ age }
</>;
}
常规处理:
javascript
import { useState } from 'react';
export default function List() {
const [listData, setListData] = useState([
{ id: '0', name: '张三三', age: 17, isWork: false, salary: null },
{ id: '1', name: '李四', age: 21, isWork: true, salary: 888 },
{ id: '2', name: '王五五', age: 36, isWork: true, salary: 8888 }
]);
// 初始数据
const initialStats = handleWorkData(listData);
const [isAllWork, setIsAllWork] = useState(initialStats.newIsAllWork);
const [workNum, setWorkNum] = useState(initialStats.newWorkNum);
// 数据处理
function handleWorkData(list) {
return {
newIsAllWork: list.every(item => item.isWork),
newWorkNum: list.filter(item => item.isWork).length
};
}
// 是否参加工作变动
function isWorkChange(e, id) {
setListData((prevData) => {
// prevData为原数据,set必须有返回值
const newData = prevData.map(item => {
if(item.id === id) {
return { ...item, isWork: e.target.checked };
}
return item;
});
const { newIsAllWork, newWorkNum } = handleWorkData(newData);
setIsAllWork(newIsAllWork);
setWorkNum(newWorkNum);
return newData;
});
}
// 渲染
let listItems = listData.map(item => {
return <li key={ item.id }>
<span>{ item.name }</span>
<input type="checkbox" checked={ item.isWork } onChange={ e => isWorkChange(e, item.id) } />
</li>;
});
return <>
<div>当前参加工作人数:{ workNum }</div>
{ isAllWork && <h3>已全部参加工作</h3> }
<ul>{ listItems }</ul>
</>;
}
状态提升(组件共享状态、父控子、子传父)
子组件状态仅影响本组件,如果需要子组件共享状态则需要状态提升- 核心:
子的公共状态提升由父控制,状态及事件由父做交互并传递给子组件,子组件调用父组件的状态及事件 - 下面是个tab切换颜色值案例:
javascript
import { useState } from 'react';
// 颜色选择按钮
function ColorButton({ color, isSelected, setSelectedColorFn }) {
return (
<button
onClick={ setSelectedColorFn }
style={ {
backgroundColor: color,
color: 'white',
padding: '10px 20px',
margin: '5px',
border: isSelected ? '3px solid black' : '3px solid transparent',
} }
>
{ color }
</button>
);
}
export default function ColorPicker() {
// 色值组
const colors = ['red', 'blue', 'green', 'greenYellow', 'purple'];
// 当前选择的颜色
const [selectedColor, setSelectedColor] = useState('red');
return (
<div style={ { padding: '20px' } }>
<h2>🎨颜色选择器</h2>
<p>当前颜色: <span style={ { color: selectedColor } }>{ selectedColor }</span></p>
{/* 所有颜色按钮 */ }
<div>
{ colors.map(color => (
<ColorButton
key={ color }
color={ color }
// 是否选中
isSelected={ selectedColor === color }
// 回调函数传递给子组件 此处名称可自定义
setSelectedColorFn={ () => setSelectedColor(color) }
/>
)) }
</div>
</div>
);
}
7、状态管理(useReducer)
useReducer主要作用是将过于分散的事件处理逻辑整合- window.location.replace("juejin.cn/user/840368...")
- 以下通过两个案例对比简述使用方法及区别
使用useState的todo
javascript
import { useState } from 'react';
let nextId = 3;
let initialTasks = [
{ id: 0, text: '代办事项1', isComplete: true, isEdit: false },
{ id: 1, text: '代办事项2', isComplete: false, isEdit: false },
{ id: 2, text: '代办事项3', isComplete: false, isEdit: false }
];
function AddSearch({ setTasks }) {
const [inputValue, setInputValue] = useState('');
function add() {
if(inputValue.trim().length > 0) {
setTasks((prevTasks) => [...prevTasks,
{
id: nextId++,
text: inputValue,
isComplete: false,
isEdit: false
}
]);
setInputValue('');
}
}
return (
<>
<input type="text"
value={ inputValue }
onChange={ (e) => setInputValue(e.target.value) }
onKeyDown={ (e) => {
if(e.key === 'Enter') add();
} }
/>
<button onClick={ add }>新增</button>
</>
);
}
export default function TaskApp() {
const [tasks, setTasks] = useState(initialTasks);
function checkboxChange(id) {
setTasks((prevTasks) => {
return prevTasks.map(task => {
if(task.id === id) {
return {
...task,
isComplete: !task.isComplete
};
}
return task;
});
});
}
function edit({ id, isEdit }) {
setTasks((prevTasks) =>
prevTasks.map(task => {
if(task.id === id) {
return {
...task,
isEdit: !isEdit
};
}
return task;
})
);
}
function del(id) {
setTasks((prevTasks) => prevTasks.filter(task => task.id !== id));
}
function textChange(text, id) {
setTasks((prevTasks) => {
return prevTasks.map(task => {
if(task.id === id) {
return { ...task, text };
}
return task;
});
});
}
let tasksList = tasks.map((task) => {
return (
<div key={ task.id }>
<input type="checkbox" checked={ task.isComplete } onChange={ () => checkboxChange(task.id) } />
{ !task.isEdit
?
<div>{ task.text }</div>
:
<input type="text"
value={ task.text }
onChange={ (e) => textChange(e.target.value, task.id) }
/> }
<div>是否已完成:{ task.isComplete ? '是' : '否' }</div>
<button onClick={ () => edit(task) }>{ task.isEdit ? '保存' : '编辑' }</button>
<button onClick={ () => del(task.id) }>删除</button>
</div>
);
});
return (
<>
<AddSearch tasks={ tasks } setTasks={ setTasks } />
{ tasksList }
</>
);
}
使用useReducer的todo
ini
import { useReducer, useState } from 'react';
let nextId = 3;
let initialTasks = [
{ id: 0, text: '代办事项1', isComplete: true, isEdit: false },
{ id: 1, text: '代办事项2', isComplete: false, isEdit: false },
{ id: 2, text: '代办事项3', isComplete: false, isEdit: false }
];
// 定义reducer函数
function tasksReducer(state, action) {
switch(action.type) {
case 'add': {
return [
...state,
{
id: action.id,
text: action.text,
isComplete: false,
isEdit: false
}
];
}
case 'edit': {
return state.map(task => {
if(task.id === action.id) {
return {
...task,
isEdit: !task.isEdit
};
}
return task;
});
}
case 'change': {
return state.map(task => {
if(task.id === action.id) {
return {
...task,
text: action.text
};
}
return task;
});
}
case 'toggle': {
return state.map(task => {
if(task.id === action.id) {
return {
...task,
isComplete: !task.isComplete
};
}
return task;
});
}
case 'delete': {
return state.filter(task => task.id !== action.id);
}
default: {
throw new Error('未知的action类型: ' + action.type);
}
}
}
function AddSearch({ dispatch }) {
const [inputValue, setInputValue] = useState('');
function add() {
if(inputValue.trim().length > 0) {
dispatch({
type: 'add',
id: nextId++,
text: inputValue
});
setInputValue('');
}
}
return (
<>
<input type="text"
value={ inputValue }
onChange={ (e) => setInputValue(e.target.value) }
onKeyDown={ (e) => {
if(e.key === 'Enter') add();
} }
/>
<button onClick={ add }>新增</button>
</>
);
}
export default function TaskApp() {
// 使用 useReducer
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);
let tasksList = tasks.map((task) => {
return (
<div key={ task.id }>
<input
type="checkbox"
checked={ task.isComplete }
onChange={ () => dispatch({ type: 'toggle', id: task.id }) }
/>
{ !task.isEdit
?
<div>{ task.text }</div>
:
<input
type="text"
value={ task.text }
onChange={ (e) => dispatch({
type: 'change',
id: task.id,
text: e.target.value
}) }
onKeyDown={ (e) => {
if(e.key === 'Enter') dispatch({ type: 'edit', id: task.id });
} }
autoFocus
/>
}
<div>是否已完成:{ task.isComplete ? '是' : '否' }</div>
<button onClick={ () => task.text.trim().length > 0 && dispatch({ type: 'edit', id: task.id }) }>
{ task.isEdit ? '保存' : '编辑' }
</button>
<button onClick={ () => dispatch({ type: 'delete', id: task.id }) }>
删除
</button>
</div>
);
});
return (
<>
<AddSearch dispatch={ dispatch } />
{ tasksList }
</>
);
}
8、组件通信
父传子
可以在前置5、组件通信props查看,详解中已概述使用方法
子传父-回调函数用法
- 父组件传递一个函数给子组件
- 子组件调用该函数并传递数据作为参数
- 父组件在函数中接收数据并更新状态
javascript
import { useState } from 'react';
// 子组件
function Child({ onSendData }) {
const [inputValue, setInputValue] = useState('');
const handleSend = () => {
// 调用父组件传递的回调函数,并传递数据
onSendData(inputValue);
};
return (
<div>
<h3>子组件</h3>
<input
value={ inputValue }
onChange={ (e) => setInputValue(e.target.value) }
placeholder="输入要传递的数据"
/>
<button onClick={ handleSend }>发送给父组件</button>
</div>
);
}
// 父组件
export default function Parent() {
const [messageFromChild, setMessageFromChild] = useState('');
// 回调函数,用于接收子组件的数据
const handleChildData = (data) => {
setMessageFromChild(data);
};
return (
<div>
<h2>父组件</h2>
<p>来自子组件的消息:{ messageFromChild }</p>
<Child onSendData={ handleChildData } />
</div>
);
}
子传父/兄弟传值-状态提升用法
- 将状态提升到
共同的父组件中,通过props传递给子组件,子组件通过回调函数修改该状态。 - 此处兄弟组件也可以
共享到相同父级的数据,通过其他子组件修改的值也会传递至兄弟元素,子传父/兄弟传值通用
javascript
import { useState } from 'react';
// 父组件
export default function Parent() {
const [sharedData, setSharedData] = useState('');
return (
<div>
<h2>共享数据:{ sharedData }</h2>
<Child text="来自ChildA的数据" onDataChange={ setSharedData } data={ sharedData } />
<Child text="来自ChildB的数据" onDataChange={ setSharedData } data={ sharedData } />
<Child text="来自ChildC的数据" onDataChange={ setSharedData } data={ sharedData } />
</div>
);
}
// 子组件
function Child({ text, data, onDataChange }) {
return (
<div>
<div>当前数据源:{ data }</div>
<button onClick={ () => onDataChange(text) }>
更新数据
</button>
</div>
);
}
子传父-状态提升用法
javascript
import { useState } from 'react';
// 父组件
export default function Parent() {
const [sharedData, setSharedData] = useState('');
return (
<div>
<h2>共享数据:{ sharedData }</h2>
<Child text="来自ChildA的数据" onDataChange={ setSharedData } data={ sharedData } />
<Child text="来自ChildB的数据" onDataChange={ setSharedData } data={ sharedData } />
<Child text="来自ChildC的数据" onDataChange={ setSharedData } data={ sharedData } />
</div>
);
}
// 子组件
function Child({ text, data, onDataChange }) {
return (
<div>
<div>当前数据源:{ data }</div>
<button onClick={ () => onDataChange(text) }>
更新数据
</button>
</div>
);
}
祖先传值/后代传祖先/数据共享(useContext)
| 特性 | 说明 |
|---|---|
createContext() |
创建一个 context 对象 |
Provider |
包裹需要共享的组件树,传入 value |
useContext(context) |
读取 context 的值 |
| 默认值 | 如果没有 Provider,读取的是 createContext 的默认值 |
DataContext.Provider组件接受一个value属性,用于传递上下文的值给后代组件。当 Provider 的 value 发生变化时,所有依赖于这个上下文的后代组件都会重新渲染。- 此处示例为方便理解仅嵌套一层,
Child只要包裹在DataContext.Provider内,无论处于多少层后代,都可以通过useContext获取到
javascript
import React, { createContext, useState, useContext } from 'react';
// 创建Context
const DataContext = createContext();
// 父组件
export default function Parent() {
const [data, setData] = useState('来自父组件的数据');
return (
<DataContext.Provider value={ { data, setData } }>
<div>
<h2>父组件接收的数据:{ data }</h2>
<Child />
</div>
</DataContext.Provider>
);
}
// 子组件
function Child() {
const { data, setData } = useContext(DataContext);
return (
<div>
<div>子组件接受的数据:{ data }</div>
<button onClick={ () => setData('来自子组件的数据') }>
更新父组件数据
</button>
</div>
);
}
建议推荐:
- 简单场景:使用回调函数最直观
- 多层嵌套:使用 Context API
- 兄弟组件通信:状态提升到共同父组件
9、useRef
①. 访问 DOM 元素
- 最常见的用途是直接访问和操作 DOM 元素。
javascript
import { useRef, useEffect } from 'react';
function TextInput() {
const inputRef = useRef(null);
useEffect(() => {
// 组件挂载后自动聚焦输入框
inputRef.current.focus();
}, []);
return <input ref={inputRef} type="text" />;
}
②. ## 存储可变值(不会触发重渲染)
- 存储一个在组件生命周期内持久化 且更改不会触发重渲染的值。
javascript
import { useRef, useState } from 'react';
function Timer() {
const [count, setCount] = useState(0);
// 存储 intervalID
const intervalRef = useRef(null);
const startTimer = () => {
intervalRef.current = setInterval(() => {
setCount(prev => prev + 1);
}, 1000);
};
const stopTimer = () => {
clearInterval(intervalRef.current);
};
return (
<div>
<p>计时: {count} 秒</p>
<button onClick={startTimer}>开始</button>
<button onClick={stopTimer}>停止</button>
</div>
);
}
10、useEffect
基本使用示例
useEffect类似于vue的生命周期+监听组合体- 通过依赖/更新/清理的形式灵活使用
javascript
import { useState, useEffect } from 'react';
export default function SimpleExample() {
// 1. 首先需要一个状态变量
const [count, setCount] = useState(0);
// 2. 最简单的 useEffect 结构
useEffect(() => {
// 当 count 变化时,这里面的代码会自动执行
console.log(`Count更新了!现在是: ${count}`);
// 更新页面标题(一个常见的副作用)
document.title = `点击次数: ${count}`;
// 3. 可选的"清理函数"(这里不需要,所以不写 return)
// 如果需要清理(如移除事件监听、清除定时器),在这里返回一个函数
// return () => { ...清理代码... }
}, [count]); // 这里是"依赖数据",可以是一个也可以是多个,以数组的形式传递
// 第二个参数分别有以下三种情况
// [count] 表示:当 count 变化时,执行上面的副作用函数
// [] 空数组表示:只在组件第一次渲染时执行一次
// 不写第二个参数:每次组件渲染都执行(很少用)
return (
<div>
<h1>useEffect示例</h1>
<p>当前计数: {count}</p>
<button onClick={() => setCount(count + 1)}>
点击增加
</button>
</div>
);
}
场景一:页面加载时获取数据
javascript
import { useEffect, useState } from "react";
export default function Fridge() {
const [foods, setFoods] = useState([]);
useEffect(() => {
console.log("📦 打开冰箱门,查看食物...");
// 模拟获取数据
setTimeout(() => {
setFoods(["苹果", "牛奶", "鸡蛋"]);
}, 1000);
}, []); // [] 表示只执行一次
return <div>冰箱里有: { foods.join(', ') }</div>;
}
场景二:值变化时更新
- 类似场景如:依赖不同id获取展示详情
- 类似场景如:tab切换模块组件等
javascript
import { useState, useEffect } from "react";
export default function WeatherApp() {
const [temperature, setTemperature] = useState(25);
const [clothes, setClothes] = useState("");
useEffect(() => {
console.log(`🌡️ 温度变成 ${ temperature }°C 了`);
if(temperature > 30) {
setClothes("穿短袖");
}
else if(temperature > 20) {
setClothes("穿长袖");
}
else {
setClothes("穿外套");
}
}, [temperature]); // [temperature] 表示温度变化就执行,即依赖数据变化就执行
return (
<div>
<div>当前温度: { temperature }°C</div>
<div>穿衣建议: { clothes }</div>
<button onClick={ () => setTemperature(temperature + 5) }>升温</button>
<button onClick={ () => setTemperature(temperature - 5) }>降温</button>
</div>
);
}