1.组件通信方式:
父组件向子组件:props
const Child=props=>{
return <p>{props.name}</p>
}
const Parent=()=>{
return <Child name="hello"></Child>
}
子组件向父组件:props+回调
const Child= props =>{
const fn= msg =>{
return () =>{props.callback(msg)}
}
return( <button onClick={fn('你好!')}>你好</button> )
}
class Parent extends Component{
callback(msg){
console.log(msg)
}
render(){
return <Child callback={this.callback.bind(this)}></Child>
}
}
兄弟组件:找到共同的父节点,结合上面两种方式
跨层级通信:context
创建一个context对象
const MyContext=React.createContext('light')
class App extends React.Component{
return (
<MyContext.Provider value="dark">
<Toolbar/>
</MyContext.Provider>
);
}
//中间的组件不必指明往下传递
发布订阅模式:
非嵌套关系的组件:全局状态管理工具rudex
2.路由
<BrowserRouter>
<HashRouter>
通过监听hashchange事件感知hash变化
3.Hooks
为函数组件补齐类组件的功能
常用Hooks:
按功能分类:
数据更新驱动:useState useReducer
执行副作用:useEffect useLayoutEffect
状态获取与传递:useContext useRef
状态派生与保存:useMemo useCallback
①useState
useState返回的是数组,解构的时候可以对数组中的元素命名
const [state,setState]=useState(initialState)
set函数是异步更新
②useEffect
执行一段与当前组件渲染无关的副作用代码
接受两个参数:回调函数和依赖数组
useEffect(() => {
// 副作用逻辑
}, [dependencies]); // 依赖数组
a.不传依赖数组,每次渲染时都执行
b.依赖数组为空数组时,只在挂载时执行
c.有依赖项,首次挂载和依赖数组改变时执行
清理机制:
useEffect可以返回一个清理函数
执行时机:
1组件卸载时:组件从DOM中移除时,执行最后一次副作用的清理函数
2副作用重新执行前:依赖数组发生变化,先执行副作用的清理函数,再执行新的副作用
典型应用场景:
useEffect(() => {
const interval = setInterval(() => {
console.log('定时器运行中');
}, 1000);
// 清理函数:组件卸载或依赖项变化时清除定时器
return () => {
clearInterval(interval);
console.log('定时器已清除');
};
}, []); // 空依赖数组表示只在挂载时创建定时器
react渲染流程整体分为两个阶段:render阶段和commit阶段
render阶段就是reconcile 的 vdom 转 fiber 的过程
render function执行后产生React Element对象,也就是vdom
vdom会转换为fiber解构
commit 阶段就是具体操作 dom,以及执行副作用函数的过程
分为三个小阶段:before mutation,mutation,layout
mutation阶段操作dom
③useLayoutEffect
同步执行
在DOM更新之后,浏览器绘制之前
④useMemo
用于缓存计算值
接受两个参数:一个计算函数和一个依赖数组
避免在每次渲染时重新计算复杂的值
减少不必要的计算
⑤useCallback
用于缓存函数
接受两个参数:一个函数和一个依赖数组
依赖数组的值发生变化时,重新创建函数
⑥useContext
用于跨层级通信
⑦useRef
接收一个状态作为初始值,返回一个ref对象
const cur = React.useRef(initState)
console.log(cur.current)
4.状态逻辑复用
状态逻辑复用:
将组件中可复用的状态管理逻辑(如数据请求、表单处理、定时器等)抽离出来,
供多个组件共享,减少代码冗余提高维护性
常见方案:HOC 自定义Hook Render Props
HOC
高阶组件是一个函数,接收一个组件作为参数,返回一个新的增强组件
通过包装组件的方式,注入复用的状态逻辑和props
// 定义高阶组件:处理计数器逻辑
function withCounter(WrappedComponent) {
return class extends React.Component {
state = { count: 0 };
increment = () => this.setState({ count: this.state.count + 1 });
render() {
// 将状态和方法通过 props 传递给被包装组件
return (
<WrappedComponent
count={this.state.count}
increment={this.increment}
{...this.props} // 透传外部 props
/>
);
}
};
}
// 使用高阶组件
const CounterButton = withCounter(({ count, increment, label }) => (
<button onClick={increment}>
{label}: {count}
</button>
));
// 复用:不同组件共享计数器逻辑
<CounterButton label="点赞数" />
<CounterButton label="收藏数" />
缺点:可能导致包装地狱(多层HOC嵌套)
静态方法需手动赋值,props可能被覆盖
自定义Hook
自定义Hook是一个以use开头的函数,内部可以调用其他Hook
组件通过调用自定义hook直接获取状态和方法
// 定义自定义 Hook:处理计数器逻辑
function useCounter(initialCount = 0) {
const [count, setCount] = React.useState(initialCount);
const increment = () => setCount(prev => prev + 1);
const decrement = () => setCount(prev => prev - 1);
return { count, increment, decrement }; // 返回状态和方法
}
// 组件中使用
function CounterDisplay() {
const { count, increment } = useCounter(); // 直接调用 Hook 获取逻辑
return <button onClick={increment}>计数:{count}</button>;
}
function DoubleCounter() {
const { count, increment } = useCounter(10); // 可传入初始值,灵活复用
return <button onClick={increment}>翻倍计数:{count * 2}</button>;
}
优势:简洁直观,可组合多个hook
缺点:必须遵循hook原则,只能在函数组件或自定义hook中使用,不能在条件语句中使用
Render Props
通过一个返回React元素的prop,将复用的状态逻辑传递给组件
组件通过调用该prop渲染内容,接收逻辑中的状态和方法
// 定义 Render Props 组件:处理鼠标位置逻辑
class MouseTracker extends React.Component {
state = { x: 0, y: 0 };
handleMouseMove = (e) => {
this.setState({ x: e.clientX, y: e.clientY });
};
componentDidMount() {
window.addEventListener('mousemove', this.handleMouseMove);
}
componentWillUnmount() {
window.removeEventListener('mousemove', this.handleMouseMove);
}
render() {
// 调用 render prop,将状态传递给外部
return this.props.render(this.state);
}
}
// 使用 Render Props
function MousePosition() {
return (
<MouseTracker
render={({ x, y }) => ( // 通过 render prop 接收状态
<div>鼠标位置:({x}, {y})</div>
)} /> );
}
// 复用:不同组件共享鼠标位置逻辑
function CatOnMouse() {
return (
<MouseTracker
render={({ x, y }) => (
<img
src="cat.png"
style={{ position: 'absolute', left: x, top: y }}
alt="跟随鼠标的猫"
/> )} /> );
}
5.React.Router的实现
React 生态中用于实现单页应用(SPA)路由管理的核心库
在不刷新页面的情况下,根据 URL 变化渲染对应的组件,提供导航、路由参数、嵌套路由等功能
路由模式:基于浏览器API实现URL监听和修改
单页应用的路由本质:改变URL但不刷新页面
hash模式
利用URL中的哈希(#及后面的部分)作为路由标识
改变哈希不会触发页面刷新,且浏览器会记录哈希变化的历史
实现:
通过 window.location.hash 读取当前哈希值
监听 hashchange 事件,哈希变化时触发路由更新
导航时(如点击 Link 组件),通过修改 window.location.hash 改变 URL,避免页面刷新
history模式(browserrouter)
基于 HTML5 的 History API(window.history),通过 pushState、replaceState 等方法修改 URL 路径(不含 #),且不触发页面刷新
实现:
通过 window.history.pushState() 或 replaceState() 改变 URL(只修改历史记录,不发送请求)
监听 popstate 事件(用户点击前进 / 后退按钮触发),感知 URL 变化并更新路由
导航时,通过 history.push() 或 history.replace() 封装 pushState/replaceState,实现无刷新跳转
核心组件:
路由更新机制:
状态变化触发组件重渲染
当URL变化时:
URL改变,感知变化,更新location
重新匹配路由,渲染匹配组件