一、React入门:从"搭积木"开始理解组件化
1.1 为什么说React像乐高?
刚接触React时,都说"组件化开发就像搭积木",我盯着满屏的JSX语法一脸懵。直到自己写出第一个组件才恍然大悟——原来每个按钮都是独立封装的乐高积木块!
// 第一个组件:会卖萌的按钮
function CuteButton({ text }) {
return (
<button style={{
padding: '10px 20px',
borderRadius: '20px',
background: 'pink',
border: 'none'
}}>
🎀 {text} 🎀
</button>
);
}
// 使用组件
<CuteButton text="点击领取小猫" />
新手踩坑实录:
有次我把组件命名为button(全小写),结果死活渲染不出来。后来才明白组件名必须大写字母开头,这是JSX的强制约定!这种细节在初学阶段最容易让人抓狂😫
1.2 JSX:当HTML遇见JavaScript
第一次看到JSX时,我的内心是崩溃的——这既不像HTML也不像纯JS。直到在项目中实现动态列表,才体会到它的妙处:
function TodoList() {
const todos = ['买猫粮', '撸代码', '给主子铲屎'];
return (
<ul className="todo-list">
{todos.map((item, index) => (
// 注意这里需要key属性!
<li key={index} style={{ color: index === 0 ? 'red' : '#333' }}>
🐾 {item}
</li>
))}
</ul>
);
}
避坑指南:
列表渲染必须加key属性
行内样式要写成对象形式:{{ }}双括号不是手抖!
二、组件进阶:打造高复用性代码块
2.1 组件通信的三种姿势
2.1.1 Props传参:父子组件对话
// 父组件
<CatProfile
name="奥利奥"
age={2}
onFeed={() => console.log('投喂成功🍗')}
/>
// 子组件
function CatProfile({ name, age, onFeed }) {
return (
<div>
<h3>{name} · {age}岁</h3>
<button onClick={onFeed}>喂食按钮</button>
</div>
);
}
2.1.2 Context:跨层级传值
实现主题切换的经典场景:
const ThemeContext = createContext('light');
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar() {
const theme = useContext(ThemeContext);
return <div style={{ background: theme === 'dark' ? '#333' : '#fff' }}>...</div>;
}
2.1.3 自定义Hook:更优雅的复用
封装一个获取鼠标位置的Hook:
function useMousePosition() {
const [position, setPosition] = useState({ x: 0, y: 0 });
useEffect(() => {
const updatePosition = e => setPosition({ x: e.clientX, y: e.clientY });
window.addEventListener('mousemove', updatePosition);
return () => window.removeEventListener('mousemove', updatePosition);
}, []);
return position;
}
// 在组件中使用
function CursorTracker() {
const { x, y } = useMousePosition();
return <div>鼠标坐标:({x}, {y})</div>;
}
三、状态管理:从useState到全局状态管控
3.1 状态管理进化论
方案 适用场景 新手友好度
useState 简单组件内部状态 ⭐⭐⭐⭐⭐
useReducer 复杂状态逻辑 ⭐⭐⭐⭐
Context API 跨组件层级传递 ⭐⭐⭐
Redux 大型应用全局状态 ⭐⭐
3.2 购物车案例实战
function Cart() {
const [items, dispatch] = useReducer(cartReducer, []);
// 加入购物车
const addItem = item => dispatch({ type: 'ADD_ITEM', payload: item });
// 移除商品
const removeItem = id => dispatch({ type: 'REMOVE_ITEM', payload: id });
return (
<div>
{items.map(item => (
<div key={item.id}>
{item.name} -
<button onClick={() => removeItem(item.id)}>🗑️</button>
</div>
))}
<button onClick={() => addItem({ id: Date.now(), name: '猫罐头' })}>
添加商品
</button>
</div>
);
}
function cartReducer(state, action) {
switch (action.type) {
case 'ADD_ITEM':
return [...state, action.payload];
case 'REMOVE_ITEM':
return state.filter(item => item.id !== action.payload);
default:
return state;
}
}
新手常见误区:
直接修改state对象(必须返回新对象!)
在条件语句或循环中使用Hook
忘记添加依赖项导致useEffect无限循环