react学习笔记
警告
本教程使用了tailwindcss,如何使用?
react是一个javascript库,与vue不同,react是一个组件化的库,vue是一个MVVM的库。
特点:
1.声明式设计 −React采用声明范式,可以轻松描述应用。
2.高效 −React通过对DOM的模拟,最大限度地减少与DOM的交互。
3.灵活 −React可以与已知的库或框架很好地配合。
4.JSX − JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。
5.组件 − 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。
6.单向响应的数据流 − React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。
1. 如何编写一个jsx文件
import {useState} from "md/framework/react/react";
function Home() {
const [count, setCount] = useState(0);
const btnStyle = {
backgroundColor: "transparent",
color: "#000",
padding: "10px",
borderRadius: "5px",
cursor: "pointer",
margin: "10px",
boxShadow: "0 0 5px rgba(0,0,0,0.3)",
};
return (
<>
<div
className="home"
style={btnStyle}
onClick={() => setCount(count + 1)}
>
<span>{count}</span>
</div>
</>
);
}
export default Home;警告
- 逻辑不越界:声明和逻辑等操作要在函数内完成
- 返回值不缺失:必须得有返回值
<></>类似于<div></div>,可以不写标签,但必须得有返回值- 事件处理:传递的是一个函数的引用,而不是函数结果 如:
onClick={() => setCount(count + 1)}而不能是onClick={setCount(count + 1)}
诺想不这么麻烦,可以定义一个方法:jsxconst handleClick = () => { setCount(count + 1) } <div className="home" style={btnStyle} onClick={handleClick} > <span>{count}</span> </div>
useState
useState允许你在函数组件中使用局部状态。它返回一个状态值和更新该状态值的函数。
import {useState} from "react";
function UseState() {
const [count, setCount] = useState(0);
const [list, setList] = useState([0, 1]);
return (
<>
<h1>{count}</h1>
<ul>
{list.map((item, index) => (
<li key={index}>
{item}
</li>
))}
</ul>
<div>
<button
onClick={() => setCount(count + 1)}
>
+1
</button>
<button
onClick={() => setList([...list, list.length])}
>
数组增加
</button>
</div>
</>
);
}
export default UseState;警告
react的核心概念是【不可变数据】,react使用的是浅比较,只有数据完全变化才会重新渲染。
浅比较:
- 对于基础类型,会比较数值和数据类型,如:1===1,1==='1'
- 对于引用类型,还会比较内存地址,如:{}==={}
useEffect
用于监听数据变化,并执行副作用。
// 语法:
useEffect(() => {
// ...需要执行的副作用代码
return () => {
// 可选的清理代码
};
}, [依赖项数组]);import {useState, useEffect} from "react";
export default function UseEffect() {
const [count, setCount] = useState(0);
const [old, setOldCount] = useState(0);
const [newCount, setNewCount] = useState(0);
useEffect(() => {
//更新时执行
setNewCount(count);
return () => {
// 当组件卸载时或当前 `effect` 依赖变化、下一次 `effect` 执行前
setOldCount(count);
};
}, [count]);
return (
<>
<div className=" box">{count}</div>
<div className=" box flex my-10 gap-5">
<div className=" box">旧值:{old}</div>
<div className=" box">新值:{newCount}</div>
</div>
<button
className=" p-5 py-2 bg-amber-100 my-5 rounded-xl"
onClick={() => setCount(count + 1)}
>
增加
</button>
</>
);
}useContext()&&createContext()
用于跨组件传递数据。
import {createContext, useContext} from "react";
// 1. 创建上下文
const MyContext = createContext("默认数据");
// 2. 上层组件提供数据
function Parent() {
return (
<MyContext.Provider value="共享数据">// 提供数据
<Child/>
</MyContext.Provider>
);
}
// 3. 下层组件消费数据
function Child() {
const value = useContext(MyContext); // 读取上下文数据
return <div className="box">{value}</div>; // 渲染:共享数据 (诺父组件未提供数据,则使用默认数据)
}
export default Parent;useReducer
useReducer 是 useState 的替代方案,适用于复杂的状态逻辑。它接受一个 reducer 函数和初始状态,返回当前状态和 dispatch 函数。
import {useReducer} from "react";
// 定义 reducer 函数
function counterReducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
case 'reset':
return {count: 0};
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(counterReducer, {count: 0});
return (
<>
<div>Count: {state.count}</div>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'reset'})}>重置</button>
</>
);
}
export default Counter;使用场景:
- 状态逻辑复杂,有多个子值
- 下一个状态依赖于前一个状态
- 需要优化性能(可以传递 dispatch 而不是回调函数)
useMemo
useMemo 用于缓存计算结果,只有当依赖项变化时才重新计算。
import {useState, useMemo} from "react";
function ExpensiveComponent({items}) {
// 只有 items 变化时才重新计算
const sortedItems = useMemo(() => {
console.log('重新计算排序');
return items.sort((a, b) => a - b);
}, [items]);
return (
<ul>
{sortedItems.map(item => (
<li key={item}>{item}</li>
))}
</ul>
);
}
export default ExpensiveComponent;使用场景:
- 昂贵的计算操作
- 避免子组件不必要的重新渲染(配合 React.memo)
useCallback
useCallback 用于缓存函数引用,只有当依赖项变化时才返回新的函数。
import {useState, useCallback, memo} from "react";
const Child = memo(({onClick, name}) => {
console.log(`${name} 组件重新渲染`);
return <button onClick={onClick}>点击</button>;
});
function Parent() {
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
// 缓存函数,避免 Child 组件不必要的重新渲染
const handleClick1 = useCallback(() => {
setCount1(count1 + 1);
}, [count1]);
const handleClick2 = useCallback(() => {
setCount2(count2 + 1);
}, [count2]);
return (
<>
<Child onClick={handleClick1} name="Child1" />
<Child onClick={handleClick2} name="Child2" />
<div>Count1: {count1}, Count2: {count2}</div>
</>
);
}
export default Parent;使用场景:
- 将函数作为 props 传递给子组件
- 作为其他 Hook 的依赖项
useRef
useRef 用于创建一个可变的引用对象,返回的 ref 对象在组件的整个生命周期内保持不变。
import {useRef, useEffect} from "react";
function RefExample() {
const inputRef = useRef(null);
const countRef = useRef(0);
useEffect(() => {
// 聚焦输入框
inputRef.current?.focus();
}, []);
const handleClick = () => {
countRef.current += 1;
console.log('点击次数:', countRef.current);
// 注意:修改 countRef.current 不会触发重新渲染
};
return (
<>
<input ref={inputRef} type="text" />
<button onClick={handleClick}>点击</button>
</>
);
}
export default RefExample;使用场景:
- 访问 DOM 元素
- 存储可变值(不触发重新渲染)
- 保存前一个值
useImperativeHandle
useImperativeHandle 用于在使用 ref 时自定义暴露给父组件的实例值。
import {forwardRef, useImperativeHandle, useRef} from "react";
const Child = forwardRef((props, ref) => {
const inputRef = useRef(null);
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current?.focus();
},
getValue: () => {
return inputRef.current?.value;
}
}));
return <input ref={inputRef} type="text" />;
});
function Parent() {
const childRef = useRef(null);
const handleFocus = () => {
childRef.current?.focus();
};
const handleGetValue = () => {
const value = childRef.current?.getValue();
console.log('输入值:', value);
};
return (
<>
<Child ref={childRef} />
<button onClick={handleFocus}>聚焦</button>
<button onClick={handleGetValue}>获取值</button>
</>
);
}
export default Parent;自定义 Hooks
自定义 Hook 是一个函数,其名称以 "use" 开头,函数内部可以调用其他的 Hook。
import {useState, useEffect} from "react";
// 自定义 Hook:用于获取窗口大小
function useWindowSize() {
const [size, setSize] = useState({
width: window.innerWidth,
height: window.innerHeight
});
useEffect(() => {
const handleResize = () => {
setSize({
width: window.innerWidth,
height: window.innerHeight
});
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return size;
}
// 使用自定义 Hook
function WindowSizeDisplay() {
const {width, height} = useWindowSize();
return (
<div>
窗口大小: {width} x {height}
</div>
);
}
export default WindowSizeDisplay;组件通信
1. 父子组件通信
父传子(Props):
// 父组件
function Parent() {
const message = "Hello from Parent";
return <Child message={message} />;
}
// 子组件
function Child({message}) {
return <div>{message}</div>;
}子传父(回调函数):
// 父组件
function Parent() {
const [count, setCount] = useState(0);
const handleChildClick = (value) => {
setCount(value);
};
return <Child onClick={handleChildClick} />;
}
// 子组件
function Child({onClick}) {
return (
<button onClick={() => onClick(100)}>
传递值给父组件
</button>
);
}2. 兄弟组件通信
通过共同的父组件进行状态提升:
function Parent() {
const [sharedData, setSharedData] = useState('');
return (
<>
<Child1 onUpdate={setSharedData} />
<Child2 data={sharedData} />
</>
);
}
function Child1({onUpdate}) {
return (
<button onClick={() => onUpdate('来自 Child1 的数据')}>
更新数据
</button>
);
}
function Child2({data}) {
return <div>接收到的数据: {data}</div>;
}3. 跨组件通信(Context)
import {createContext, useContext, useState} from "react";
const DataContext = createContext();
function App() {
const [data, setData] = useState('共享数据');
return (
<DataContext.Provider value={{data, setData}}>
<Component1 />
<Component2 />
</DataContext.Provider>
);
}
function Component1() {
const {data, setData} = useContext(DataContext);
return (
<div>
<p>{data}</p>
<button onClick={() => setData('Component1 更新的数据')}>
更新数据
</button>
</div>
);
}
function Component2() {
const {data} = useContext(DataContext);
return <div>接收到的数据: {data}</div>;
}性能优化
1. React.memo
用于函数组件,类似于 PureComponent,只在 props 变化时重新渲染。
import {memo} from "react";
const ExpensiveComponent = memo(({name, count}) => {
console.log('组件重新渲染');
return (
<div>
{name}: {count}
</div>
);
}, (prevProps, nextProps) => {
// 自定义比较函数(可选)
return prevProps.name === nextProps.name &&
prevProps.count === nextProps.count;
});
export default ExpensiveComponent;2. useMemo 和 useCallback
配合使用避免不必要的计算和重新渲染。
function OptimizedParent({items}) {
const [filter, setFilter] = useState('');
// 缓存计算结果
const filteredItems = useMemo(() => {
return items.filter(item => item.includes(filter));
}, [items, filter]);
// 缓存函数引用
const handleClick = useCallback(() => {
console.log('点击事件');
}, []);
return (
<>
<input
value={filter}
onChange={(e) => setFilter(e.target.value)}
/>
<ExpensiveList items={filteredItems} onClick={handleClick} />
</>
);
}3. 代码分割(React.lazy)
import {lazy, Suspense} from "react";
// 懒加载组件
const LazyComponent = lazy(() => import('./LazyComponent'));
function App() {
return (
<Suspense fallback={<div>加载中...</div>}>
<LazyComponent />
</Suspense>
);
}生命周期对比
类组件生命周期
| 生命周期方法 | 函数组件等价物 |
|---|---|
componentDidMount | useEffect(() => {}, []) |
componentDidUpdate | useEffect(() => {}) |
componentWillUnmount | useEffect(() => { return () => {} }) |
函数组件生命周期示例
import {useEffect} from "react";
function LifecycleExample() {
useEffect(() => {
// componentDidMount
console.log('组件挂载');
return () => {
// componentWillUnmount
console.log('组件卸载');
};
}, []);
useEffect(() => {
// componentDidUpdate
console.log('组件更新');
});
return <div>生命周期示例</div>;
}注意事项
- 不要在循环、条件或嵌套函数中调用 Hooks
- 只在 React 函数组件或自定义 Hook 中调用 Hooks
- 使用依赖数组时要包含所有依赖项
- 避免在 useEffect 中修改 state 导致无限循环
- 合理使用 useMemo 和 useCallback,不要过度优化
最后更新:2025年