React由Meta公司研发,用于构建Web和原生交互界面的库。
React 官方中文文档
查看JSX
(一)React组件
-
用户界面的一部分,通俗的来讲,最小的元素组成的单元,可以实现部分逻辑与功能
-
房子的门就可以看成一个组件,由木板和木方组成,有一定功能和作用,多个组件组合就可以形成房子了。
-
组件之间可以相互嵌套,重复使用
React组件
-
React中,组件就是一个首字母大写的函数,内部存放组件的逻辑和视图UI,使用组件只需将组件当成标签写即可
-
组件也不能返回多个 JSX 标签,必须将它们包裹到一个共享的父级中,比如
<div>...</div>
或使用空的<>...</>
包裹: -
组件只负责自己的任务。 它不会更改在该函数调用前就已存在的对象或变量。
-
组件输入相同,则输出相同。 给定相同的输入,组件应该总是返回相同的 JSX。
function MyButton() {return(<button >点了我</button>);
}const App = () => {return <MyButton ></MyButton>;};
Hook(钩子函数)
hook是特殊的函数,搭配函数组件一起使用,使其功能更强大
hook使用规则
- 只能在顶层调用Hooks,不要在循环、条件判断或者嵌套函数中调用。
- 只能在React函数组件或自定义Hook中调用Hooks。
(二)useState
为组件设置state可以让组件拥有记忆,完成交互
特点
-
State 变量 用于保存渲染间的数据。
-
State setter 函数 更新变量并触发 React 再次渲染组件
-
只能在组件或[自定义 Hook]的最顶层调用
快速入门
-
在文件顶部导入useState
import { useState } from 'react';
-
创建一个useState
const [index, setIndex] = useState(0);
useState
的唯一参数是 state 变量的初始值
useState细节理解
-
状态更新是异步的
React 会将多个状态更新合并,以提高性能。因此,状态更新不会立即生效,而是会在下一次渲染时生效。
-
批量更新
在 React 的事件处理函数中,多个状态更新会被批量处理,只触发一次重新渲染。
useState更新原理
State通过setState更新,实际上State只是可读的,setState更新只是将原来的值替换掉,而并非修改原数据
-
更新State对象
通过
...对象展开符
快速跟新对象const [user, setUser] = useState({ name: 'Alice', age: 25 });// 更新年龄 const updateAge = () => {setUser(prev => ({ ...prev, age: 26 })); };
-
更新State数组
注意:即使你拷贝了数组,你还是不能直接修改其内部的元素,因为数组拷贝是浅拷贝
避免使用 (会改变原始数组) 推荐使用 (会返回一个新数组) 添加元素 push
,unshift
concat
,[...arr]
展开语法删除元素 pop
,shift
,splice
filter
,slice
替换元素 splice
,arr[i] = ...
赋值map
排序 reverse
,sort
先将数组复制一份,再排序
受控表单
使用react组件状态控制表单
const App = () => {const [value, setValue] = useState('');return (<><inputvalue={value}onChange={(e)=>setValue(e.target.value)}type="text"/></>)
};
如何构建State
- 先根据react数据驱动视图的特点,页面哪些UI状态改变会导致界面改变,就给它定义个State控制界面更新
- 然后根据构建State原则对State进行优化
构建State的原则
状态提升
要从多个子组件收集数据,或让两个子组件相互通信,请改为在其父组件中声明共享 state。父组件可以通过 props 将该 state 传回给子组件。这使子组件彼此同步并与其父组件保持同步。
(三)props
React中使用props进行参数传递
//value=0进行默认赋值,当value=null就读取默认值0
function Square({value=0, onSquareClick}) {return (<button className="square" onClick={onSquareClick}>{value}</button>);
}export default function App(){return(<div className="board-row"><Square value="1" onSquareClick={() => handleClick(0)}/><Square value="2" onSquareClick={() => handleClick(1)}/><Square value="3" onSquareClick={() => handleClick(2)}/></div>);
}
children属性
props.children
是一个特殊的属性,用于传递组件包裹的子元素(子节点)
children
可以是以下任意类型:
- 字符串:直接作为文本内容。
- React 元素:如
<div>
、自定义组件等。 - 数组:多个子元素组成的数组。
- 函数:通过函数模式实现动态渲染(即 Render Props 模式)。
- 空值:
null
、undefined
或false
(不会被渲染)。
<Child><h1>标题</h1><p>段落</p>
</Child>// Child 组件内部:
function Child(props) {return <div>{props.children}</div>; // 同时渲染标题和段落
}
(四)UseContext跨层传递参数
Context是一种跨组件层级传递数据的机制,适合全局数据(如用户信息、主题、语言等),区别于props逐层传递。
- Provider:通过
<MyContext.Provider value={data}>
向子组件树传递数据。 - Consumer:通过
useContext(MyContext)
直接获取最近一层 Provider 提供的数据。
示例
假设有 3 层组件:Parent
→ Child
→ Grandchild
,需要将用户信息从 Parent
直接传递到 Grandchild
,无需经过 Child
中转。
// UserContext.js
import { createContext } from 'react';export const UserContext = createContext({ name: '默认用户', age: 0
});
// Parent.jsx
import { UserContext } from './UserContext';
import Child from './Child';const Parent = () => {const user = { name: "小明", age: 20 };return (<UserContext.Provider value={user}><div><h2>Parent 组件</h2><Child /> {/* 子组件不需要传递任何 props! */}</div></UserContext.Provider>);
};
// Child.jsx
import Grandchild from './Grandchild';const Child = () => {return (<div><h3>Child 组件</h3><Grandchild /> {/* 同样无需传递 props */}</div>);
};
// Grandchild.jsx
import { useContext } from 'react';
import { UserContext } from './UserContext';const Grandchild = () => {const user = useContext(UserContext); // 直接获取数据return (<div><h4>Grandchild 组件</h4><p>用户名:{user.name}</p><p>年龄:{user.age}</p></div>);
};
(五)UseReducer集中状态管理
useReducer – React 中文文档
const [state, dispatch] = useReducer(reducer, initialState)
useReducer类似useState管理组件状态,而UseReudcer集中状态管理,管理方式类似“告诉reducer函数用户进行了什么操作“
参数
- initialState:初始化状态
- reducer函数:负责集中管理更新状态(组件状态更新就是在这里面)
返回值
- state:目前状态
- dispatch函数:主要负责记录用户进行了什么操作并记录值,比如点击更新按钮、点击删除按钮等
如何将State状态迁移至Reducer中
用State进行状态管理的Todo清单
function TodoList() {const [todos, setTodos] = useState([]);const [input, setInput] = useState('');const addTodo = () => {setTodos([...todos, input]);setInput('');};return (<div><inputtype="text"value={input}onChange={(e) => setInput(e.target.value)}/><button onClick={addTodo}>Add Todo</button><ul>{todos.map((todo, index) => (<li key={index}>{todo}</li>))}</ul></div>);
}
用Reducer进行状态管理的Todo清单
const initialState = {todos: [],input: '',
};function reducer(state, action) {switch (action.type) {case 'updateInput':return { ...state, input: action.payload };case 'addTodo':return { ...state, todos: [...state.todos, state.input], input: '' };default:return state;}
}function TodoList() {const [state, dispatch] = useReducer(reducer, initialState);return (<div><inputtype="text"value={state.input}onChange={(e) => dispatch({ type: 'updateInput', payload: e.target.value })}/><button onClick={() => dispatch({ type: 'addTodo' })}>Add Todo</button><ul>{state.todos.map((todo, index) => (<li key={index}>{todo}</li>))}</ul></div>);
}
(六)性能提升
React.memo
用memo包裹的组件拥有记忆缓存,当props没有改变该组件不会重新渲染
React.memo(function MyComponent(props){},可选参数)
特点:
- props与上一次相同(是同一个),如果props是对象或者数组,重新创建一个相同的新的,React也会认为不同,所以通常useMemo、useCallback一起使用
- useContext多层传参也会导致Memo包裹的组件重新渲染,所以一般在外层接收作为props传入
useMemo
useMemo(函数,依赖数组),useMemo组成通常是函数和依赖数组,可以在每次重新渲染缓存计算结果(函数的返回值)
-
函数是任意不带参的函数(一般用箭头函数),可返回任意值
-
依赖数组:一般是props或useState
const TodoList=useMemo(()=>todo(tab,list),[tab,list])
useCallback
useCallback用来缓存函数,当依赖数组不发生改变时,函数不变,效果类似useMemo
useCallback(fn,dependencies)
区别useMemo和useCallback
区别 | useMemo | useCallback |
---|---|---|
主要用途 | 用于记忆计算结果 | 用于记忆定义函数 |
返回值 | 返回计算结果 | 返回记忆函数 |
使用场景 | 避免昂贵的计算 | 避免不必要的子组件重新渲染 |