前言
React Developer Tools(React 开发者工具),这是一个由 Meta 创建的浏览器扩展插件,全球有 300 万人使用。今天我们将通过这个工具带你学习一下 React 调试技能——检查组件、state和props,跟踪渲染的性能
这比浏览器控制台打印日志更高端。
如何使用 React 开发者工具
React Developer Tools 允许我们:
- 查看 React 组件树。
- 检查并更新树中任何组件的 state/props。
- 跟踪组件呈现的时间。
- 检测组件被重新渲染的原因。
有了这些功能,我们能够毫不费力地优化代码、查找错误或查明其他问题。
安装
我们将以 Chrome 展开设置
- 访问 Chrome 插件页面
- 单击添加到 Chrome。
- 在出现的弹出窗口中单击添加扩展程序。
- 等待下载完成。
- 单击浏览器右上角的扩展(拼图)图标。
- 单击图钉图标可轻松访问扩展程序。
现在,无论何时您访问使用 React 的网站,你可以看到扩展图标:
从左到右,显示的图标状态分别是:
- 使用 React 的生产构建(例如, reactjs.org/ )。
- 使用旧版本的 React(例如 mozilladevelopers.github.io/playground/ )。
- 不使用 React(例如 Google)。
- 开发环境的一部分(在我们的测试应用程序中工作时,我们会看到这个标志)。
创建测试应用
create-react-app
创建一个 react 应用:
npx create-react-app app-to-debug
完成后,进入到应用的根文件夹并启动新的 React 应用程序:
cd app-to-debug
npm start
编译后,您的应用会出现在您的浏览器中:
右上角的 React Developer Tools 图标现在表明我们正在开发环境中工作。
使用 React 开发者工具的实用调试方法
首先,打开开发人员控制台(在 Mac 上为 Option + ⌘ + J,在 Windows/Linux 上为 Shift + CTRL + J)。有多个选项卡可用(元素、控制台等)。
这个阶段我们需要看的是Components:
此时只有一个组件,因为我们的测试应用程序只有一个组件呈现 App
(请参阅 src/index.js
)。单击组件以显示其 prop、使用的 react-dom
版本和源文件。
跟踪组件状态
让我们从最常使用的功能开始:检查/编辑组件的状态。我们将用包含三个状态部分的简单登录表单替换 React 占位符主页:
- 用户名-字符串、
- 密码-字符串
- “Remember me” - 布尔值。
在 src
文件夹中,删除 App.css
、 App.test.js
和 logo.svg
,然后添加一个新的 LoginForm.js
文件,如下所示:
import { useState } from "react";
const LoginForm = () => {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [rememberMe, setRememberMe] = useState(false);
return (
<form
style={{
display: "flex",
flexDirection: "column",
gap: "8px 0",
padding: 16,
}}
>
<input
name="username"
placeholder="Username"
type="text"
value={username}
onChange={(e) => setUsername(e.currentTarget.value)}
/>
<input
name="password"
placeholder="Password"
type="password"
value={password}
onChange={(e) => setPassword(e.currentTarget.value)}
/>
<div>
<input
id="rememberMe"
name="remember_me"
type="checkbox"
checked={rememberMe}
onChange={() => setRememberMe(!rememberMe)}
/>
<label htmlFor="rememberMe">Remember me</label>
</div>
<input type="submit" name="login" value="Log in" />
</form>
);
};
export default LoginForm;
我们使用命名组件 ( const LoginForm => …
) 在开发工具中查看它的名称。匿名组件显示为 Unknown
。
LoginForm
组件将是我们的调试目标,所以让我们在 App.js
中渲染它:
import LoginForm from "./LoginForm";
const App = () => {
return <LoginForm />;
};
export default App;
返回到组件选项卡打开的浏览器窗口。现在,在 App
组件旁边,您将看到 LoginForm
组件。单击 LoginForm
将显示我们使用 useState
挂钩创建的所有状态项。由于我们还没有输入任何文本或复选框输入,我们看到两个空字符串和 false
:
在用户名和密码字段中输入任何内容或单击复选框以查看调试窗口更新中的值:
这里,我们设置状态变量没有名称。他们都被称为 State
。这是该工具的预期行为,因为 useState
仅接受值参数(在我们的示例中为 ""
或 false
)。
因为 React 对这个状态项的名称一无所知。
useDebugValue
解决了这个问题。它可以设置自定义挂钩的显示名称。例如,您可以为自定义usePassword
挂钩设置显示名称Password
。
监控组件的 Props
我们不仅可以监控 state 变化,还可以监控组件 Props。我们将首先修改 LoginForm
:
const LoginForm = ({ defaultUsername, onSubmit }) => {
const [username, setUsername] = useState(defaultUsername);
const [password, setPassword] = useState("");
const [rememberMe, setRememberMe] = useState(false);
return (
<form
style={{
display: "flex",
flexDirection: "column",
gap: "8px 0",
padding: 16,
}}
onSubmit={onSubmit}
>
// ...
上面的代码将添加一个 defaultUsername
属性以在初始加载时填写用户名,并添加 onSubmit
属性提交表单操作。我们还必须在 App
中设置这些属性的默认值:
const App = () => {
return <LoginForm defaultUsername="foo@bar.com" onSubmit={() => {}} />;
};
进行更改后重新加载页面,您将在Components选项卡中看到 Props :
您还可以在不更改任何代码的情况下进行。单击Components中的 state/props 值并设置所需的值。
测试性能
如果您遇到应用程序正常运行但运行缓慢的情况,React Developer Tools 可以识别某些性能问题。
可以突出显示 DOM 更新。单击Components选项卡右上角的齿轮图标。
您会看到一个带有四个选项卡的弹出窗口。单击General并选中Highlight updates when components render复选框。开始在密码字段中输入,您的表单将被绿色/黄色框包裹。每秒执行的更新越多,帧变得越突出。
对于更详细的性能分解,我们可以从Profiler选项卡观察。
在 Profiler 选项卡中,您会在左上角看到一个蓝色圆圈。这是一个开始分析您的应用程序的按钮。一旦你点击它,所有的 state/props 更新都会被跟踪。然而,在执行此步骤之前,我们将单击选项卡右上角的齿轮图标并选中Record why each component rendered while profiling复选框。它将通过更新的解释扩展分析器的功能。
配置现已完成,
关闭设置覆盖并单击蓝色圆圈按钮。开始在密码字段中输入并选择Remember me框。然后再次单击圆圈按钮停止分析并查看结果。
在分析结果中,您应该看到 LoginForm
组件的逐项更新。我们的示例显示了九次更新:密码字段中的每个字符八次,Remember me复选框一次。如果您单击任何更新,您将获得渲染发生原因的解释。例如,第一个渲染显示“Hook 2 changed”。
我们来看 LoginForm
组件的第二个钩子:
const [password, setPassword] = useState("");
我们的结果是正确的,因为第二个钩子负责密码状态管理。如果你点击最后一个渲染,它会显示“Hook 3 changed”,因为我们的第三个 hook 处理Remember me状态。
查看 React useReducer
和 Context
上面的示例提供了简单场景的一瞥。然而,React 的 API 包含更复杂的特性,例如 Context
和 useReducer
。
首先,我们必须添加一个包含上下文的文件。我们要创建的上下文将用于登录用户并提供登录操作的信息。我们将创建包含以下内容的 AuthenticationContext.js
文件:
import { useCallback, useContext, useReducer } from "react";
import { createContext } from "react";
const initialState = {
loading: false,
token: undefined,
error: undefined,
};
const AuthenticationContext = createContext({
...initialState,
logIn: () => {},
});
const reducer = (state, action) => {
switch (action.type) {
case "LOG_IN":
return { ...state, loading: true };
case "LOG_IN_SUCCESS":
return { ...state, loading: false, token: action.token };
case "LOG_IN_ERROR":
return { ...state, loading: false, error: action.error };
default:
return action;
}
};
const mockAPICall = async (payload) => ({ token: "TOKEN" });
export const AuthenticationContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
const logIn = useCallback(async (payload) => {
try {
dispatch({ type: "LOG_IN" });
const response = await mockAPICall(payload);
dispatch({ type: "LOG_IN_SUCCESS", token: response.token });
} catch (error) {
dispatch({ type: "LOG_IN_ERROR", error });
}
}, []);
return (
<AuthenticationContext.Provider value={{ ...state, logIn }}>
{children}
</AuthenticationContext.Provider>
);
};
export const useAuthentication = () => useContext(AuthenticationContext);
此上下文将提供我们的身份验证逻辑的加载状态、错误、结果 ( token
) 和要执行的操作 ( logIn
)。从 reducer 函数可以看出,发起登录动作会将加载值设置为 true
。如果响应成功,将更新token
;否则,将抛出错误。
我们需要更新我们的 App.js
文件:
import { AuthenticationContextProvider } from "./AuthenticationContext";
import LoginForm from "./LoginForm";
const App = () => {
return (
<AuthenticationContextProvider>
<LoginForm defaultUsername="foo@bar.com" />
</AuthenticationContextProvider>
);
};
export default App;
您现在可以重新加载页面,打开 Components
选项卡并在组件树中查看上下文:
添加了两个节点: AuthenticationContextProvider
和 Context.Provider
。第一个是我们用来将应用程序包装在 App.js
文件中的自定义提供程序。它包含一个具有当前状态的 reducer 钩子。第二个是上下文表示,显示整个组件树中提供的确切值:
{
value: {
error: undefined,
loading: false,
token: undefined,
logIn: ƒ () {}
}
}
为了确保 React Developer Tools 可以监控 reducer 的变化并显示上下文的实际状态,我们将调整 LoginForm.js
文件:
import { useCallback, useState } from "react";
import { useAuthentication } from "./AuthenticationContext";
const LoginForm = ({ defaultUsername }) => {
const { logIn } = useAuthentication();
const [username, setUsername] = useState(defaultUsername);
const [password, setPassword] = useState("");
const [rememberMe, setRememberMe] = useState(false);
const onSubmit = useCallback(
async (e) => {
e.preventDefault();
await logIn({ username, password, rememberMe });
},
[username, password, rememberMe, logIn]
);
return (
// ...
现在,如果您返回浏览器并单击“登录”,您会看到 token
,它曾经是 undefined
,在 Context.Provider
的属性中有更新的值。
全文完
谢谢!
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 17 天