react基础概念合集

前言

什么是 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在开发环境下会:
    1. ✅ 检测废弃的生命周期方法使用
    1. ✅ 警告使用过时的字符串ref API
    1. ✅ 检测意外的副作用(组件会渲染两次)
    1. ✅ 检测过时的context API
    1. ✅ 检测不安全的副作用(未来并发特性相关)
  • 注意:StrictMode只影响开发环境,生产构建时会自动移除
常见扩展模式:
    1. 添加路由:用<BrowserRouter>包裹<App />
    1. 添加状态管理:用<Provider>包裹<App />
    1. 添加主题:用<ThemeProvider>包裹<App />
    1. 添加错误边界:用<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 prop
  • Props是只读的时间快照,每次渲染都会收到新版本的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查看,详解中已概述使用方法

子传父-回调函数用法

  1. 父组件传递一个函数给子组件
  2. 子组件调用该函数并传递数据作为参数
  3. 父组件在函数中接收数据并更新状态
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>
	);
}
相关推荐
小白探索世界欧耶!~1 天前
用iframe实现单个系统页面在多个系统中复用
开发语言·前端·javascript·vue.js·经验分享·笔记·iframe
bl4ckpe4ch1 天前
用可复现实验直观理解 CORS 与 CSRF 的区别与联系
前端·web安全·网络安全·csrf·cors
阿珊和她的猫1 天前
Webpack中import的原理剖析
前端·webpack·node.js
AI前端老薛1 天前
webpack中loader和plugin的区别
前端·webpack
一只爱吃糖的小羊1 天前
从 AnyScript 到 TypeScript:如何利用 Type Guards 与 Type Predicates 实现精准的类型锁死
前端·javascript·typescript
0思必得01 天前
[Web自动化] BeautifulSoup导航文档树
前端·python·自动化·html·beautifulsoup
脾气有点小暴1 天前
Git指令大全(常见版)
前端·git
QUST-Learn3D1 天前
geometry4Sharp Ray-Mesh求交 判断点是否在几何体内部
服务器·前端·数据库
持续升级打怪中1 天前
ES6 Promise 完全指南:从入门到精通
前端·javascript·es6