React 严格模式(Strict Mode)
1. 概述
严格模式是 React 的一个开发工具,用于突出显示应用程序中潜在的问题。它通过故意进行双重渲染来帮助发现副作用。
jsx
import { StrictMode } from "react";
function App() {
return (
<StrictMode>
<Component />
</StrictMode>
);
}
2. 主要检查项
2.1 组件双重渲染
在开发模式下,严格模式会对每个组件进行两次渲染:
jsx
function Counter() {
const [count, setCount] = useState(0);
// 在严格模式下,这个 console.log 会打印两次
console.log("render");
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
2.2 检测意外的副作用
jsx
function Example() {
const [count, setCount] = useState(0);
// ❌ 不好的做法:直接在组件中进行副作用操作
document.title = `Count: ${count}`;
// ✅ 好的做法:使用 useEffect 管理副作用
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]);
return <div>{count}</div>;
}
2.3 检测过时的 API 使用
jsx
// ❌ 过时的 API
componentWillMount() {
// 这将触发警告
}
// ✅ 推荐的替代方案
componentDidMount() {
// 这是安全的
}
3. 严格模式的特性
自动检测副作用:
- 双重调用 constructor
- 双重调用 render 方法
- 双重调用 useState 的初始化函数
识别不安全的生命周期:
- 标记废弃的生命周期方法
- 提供迁移建议
检查旧的 Context API:
- 警告使用过时的 context API
- 建议使用新的 Context API
确保可重用的状态:
jsx
function App() {
// 在严格模式下,初始化函数会被调用两次
const [state] = useState(() => {
// 应该保持幂等性
return expensiveComputation();
});
return <div>{state}</div>;
}
4. 最佳实践
- 在开发环境中启用:
jsx
if (process.env.NODE_ENV === "development") {
root.render(
<StrictMode>
<App />
</StrictMode>
);
} else {
root.render(<App />);
}
- 处理副作用:
jsx
function UserProfile() {
useEffect(() => {
// ✅ 在 useEffect 中处理副作用
const subscription = api.subscribe();
return () => {
// 清理副作用
subscription.unsubscribe();
};
}, []);
return <div>User Profile</div>;
}
- 避免副作用依赖:
jsx
// ❌ 不好的做法
function Component() {
const [data, setData] = useState();
data.someMethod(); // 直接访问可能为 undefined 的数据
// ✅ 好的做法
useEffect(() => {
if (data) {
data.someMethod();
}
}, [data]);
}
5. React Native 中的严格模式
5.1 基本支持
React Native 完全支持严格模式,使用方式与 React Web 相同:
jsx
import { StrictMode } from "react";
export default function App() {
return (
<StrictMode>
<RootComponent />
</StrictMode>
);
}
5.2 特殊注意事项
- 原生模块交互:
jsx
function NativeComponent() {
useEffect(() => {
// ✅ 原生模块的调用应该放在 useEffect 中
NativeModules.SomeModule.doSomething();
return () => {
// 清理原生模块的资源
NativeModules.SomeModule.cleanup();
};
}, []);
// ❌ 不要在组件主体中直接调用原生模块
// NativeModules.SomeModule.doSomething();
}
- 动画处理:
jsx
function AnimatedComponent() {
const fadeAnim = useRef(new Animated.Value(0)).current;
useEffect(() => {
// ✅ 在 useEffect 中启动动画
Animated.timing(fadeAnim, {
toValue: 1,
duration: 1000,
useNativeDriver: true,
}).start();
}, []);
return (
<Animated.View style={{ opacity: fadeAnim }}>
<Text>Fade In</Text>
</Animated.View>
);
}
5.3 性能考虑
由于 React Native 运行在移动设备上,需要特别注意严格模式带来的双重渲染:
jsx
function ExpensiveComponent() {
// ✅ 使用 useMemo 缓存计算结果
const expensiveValue = useMemo(() => {
return heavyComputation();
}, []);
// ❌ 避免在每次渲染时进行昂贵计算
// const expensiveValue = heavyComputation();
return <Text>{expensiveValue}</Text>;
}
5.4 调试建议
jsx
function DebugComponent() {
useEffect(() => {
// 使用 React Native 的专用调试工具
if (__DEV__) {
console.log("Component mounted");
}
return () => {
if (__DEV__) {
console.log("Component will unmount");
}
};
}, []);
}