React 负责在必要时渲染组件和钩子,以优化用户体验。它是声明性的:你告诉 React 在组件的逻辑中渲染什么,React 会找出如何最好地向用户显示它。
¥React is responsible for rendering components and Hooks when necessary to optimize the user experience. It is declarative: you tell React what to render in your component’s logic, and React will figure out how best to display it to your user.
切勿直接调用组件函数
¥Never call component functions directly
组件只能在 JSX 中使用。不要将它们作为常规函数调用。React 应该调用它。
¥Components should only be used in JSX. Don’t call them as regular functions. React should call it.
React 必须决定你的组件函数何时被调用 渲染期间。在 React 中,你可以使用 JSX 来完成此操作。
¥React must decide when your component function is called during rendering. In React, you do this using JSX.
function BlogPost() {
return <Layout><Article /></Layout>; // ✅ Good: Only use components in JSX
}
function BlogPost() {
return <Layout>{Article()}</Layout>; // 🔴 Bad: Never call them directly
}
如果组件包含钩子,则在循环或有条件地直接调用组件时很容易违反 钩子的规则。
¥If a component contains Hooks, it’s easy to violate the Rules of Hooks when components are called directly in a loop or conditionally.
让 React 编排渲染还有很多好处:
¥Letting React orchestrate rendering also allows a number of benefits:
-
组件不再只是功能。React 可以通过钩子来增强它们的功能,例如本地状态,这些功能与树中组件的身份相关联。
¥Components become more than functions. React can augment them with features like local state through Hooks that are tied to the component’s identity in the tree.
-
组件类型参与协调。通过让 React 调用你的组件,你还可以告诉它更多有关树的概念结构的信息。例如,当你从渲染
<Feed>
页面移动到<Profile>
页面时,React 不会尝试重新使用它们。¥Component types participate in reconciliation. By letting React call your components, you also tell it more about the conceptual structure of your tree. For example, when you move from rendering
<Feed>
to the<Profile>
page, React won’t attempt to re-use them. -
React 可以增强你的用户体验。例如,它可以让浏览器在组件调用之间执行一些工作,以便重新渲染大型组件树不会阻塞主线程。
¥React can enhance your user experience. For example, it can let the browser do some work between component calls so that re-rendering a large component tree doesn’t block the main thread.
-
一个更好的调试故事。如果组件是库所了解的一等公民,我们就可以构建丰富的开发者工具来在开发中进行自省。
¥A better debugging story. If components are first-class citizens that the library is aware of, we can build rich developer tools for introspection in development.
-
更高效的对账。React 可以准确决定树中的哪些组件需要重新渲染,并跳过不需要的组件。这使你的应用更快、更敏捷。
¥More efficient reconciliation. React can decide exactly which components in the tree need re-rendering and skip over the ones that don’t. That makes your app faster and more snappy.
切勿将钩子作为常规值传递
¥Never pass around Hooks as regular values
Hooks 只能在组件或钩子内部调用。切勿将其作为常规值传递。
¥Hooks should only be called inside of components or Hooks. Never pass it around as a regular value.
Hooks 允许你使用 React 功能来增强组件。它们应该始终作为函数调用,而不是作为常规值传递。这使得本地推断成为可能,或者开发者能够通过单独查看该组件来了解该组件可以执行的所有操作。
¥Hooks allow you to augment a component with React features. They should always be called as a function, and never passed around as a regular value. This enables local reasoning, or the ability for developers to understand everything a component can do by looking at that component in isolation.
违反此规则将导致 React 无法自动优化你的组件。
¥Breaking this rule will cause React to not automatically optimize your component.
不要动态改变钩子
¥Don’t dynamically mutate a Hook
钩子应尽可能为 “static”。这意味着你不应该动态地改变它们。例如,这意味着你不应该编写更高阶的钩子:
¥Hooks should be as “static” as possible. This means you shouldn’t dynamically mutate them. For example, this means you shouldn’t write higher order Hooks:
function ChatInput() {
const useDataWithLogging = withLogging(useData); // 🔴 Bad: don't write higher order Hooks
const data = useDataWithLogging();
}
Hooks 应该是不可变的并且不能被改变。不要动态改变 Hook,而是创建具有所需功能的钩子的静态版本。
¥Hooks should be immutable and not be mutated. Instead of mutating a Hook dynamically, create a static version of the Hook with the desired functionality.
function ChatInput() {
const data = useDataWithLogging(); // ✅ Good: Create a new version of the Hook
}
function useDataWithLogging() {
// ... Create a new version of the Hook and inline the logic here
}
不要动态使用钩子
¥Don’t dynamically use Hooks
也不应该动态使用钩子:例如,不是通过将钩子作为值传递来在组件中进行依赖注入:
¥Hooks should also not be dynamically used: for example, instead of doing dependency injection in a component by passing a Hook as a value:
function ChatInput() {
return <Button useData={useDataWithLogging} /> // 🔴 Bad: don't pass Hooks as props
}
你应该始终将钩子的调用内联到该组件中并处理其中的任何逻辑。
¥You should always inline the call of the Hook into that component and handle any logic in there.
function ChatInput() {
return <Button />
}
function Button() {
const data = useDataWithLogging(); // ✅ Good: Use the Hook directly
}
function useDataWithLogging() {
// If there's any conditional logic to change the Hook's behavior, it should be inlined into
// the Hook
}
这样,<Button />
就更容易理解和调试了。当以动态方式使用钩子时,它会大大增加应用的复杂性并抑制本地推断,从长远来看会降低团队的生产力。这也使得意外破坏 钩子的规则 变得更加容易,因为钩子不应有条件地调用。如果你发现自己需要模拟组件进行测试,最好模拟服务器而不是使用预设数据进行响应。如果可能的话,通过端到端测试来测试你的应用通常也更有效。
¥This way, <Button />
is much easier to understand and debug. When Hooks are used in dynamic ways, it increases the complexity of your app greatly and inhibits local reasoning, making your team less productive in the long term. It also makes it easier to accidentally break the Rules of Hooks that Hooks should not be called conditionally. If you find yourself needing to mock components for tests, it’s better to mock the server instead to respond with canned data. If possible, it’s also usually more effective to test your app with end-to-end tests.