Concepts

Context

编辑此页面

Context(上下文)提供了一种通过组件树传递数据的方法,而无需每级手动向下传递 props。


何时使用上下文

当您有一个需要共享状态的大型组件树时,可以使用上下文。使用上下文可以避免 prop drilling,实现了 props 跨层级传递而无须一层一层传递。

如果您想避免通过少数几层传递某些 props,那么在合适的情况下,调整组件层次结构可能是一个更简单的解决方案。信号通常是最简单的解决方案,因为它们可以直接导入到需要的组件中。

上下文旨在共享应用程序全局的数据或应用程序组件树中的多个组件需要经常访问的信息。上下文提供了一种跨应用程序访问状态的方法,无需通过中间层传递 props 或将它们直接导入到组件中。


创建上下文

使用 createContext 函数创建上下文。此函数有一个 Provider 属性,用于包裹您要为其提供上下文的组件树。


为子级提供上下文

要将值传递给 Provider ,您可以使用 value 属性,它可以接收任何值,包括 signal 。一旦将值传递给 Provider ,它就可供 Provider 后代的所有组件使用。

当传递单个值时,可以直接传递给 value 属性:

/context/component.jsx
import { createContext, useContext } from "solid-js";
import { MyContext } from "./create";
const Provider = (props) => (
<MyContext.Provider value="new value">{props.children}</MyContext.Provider>
);

消费上下文

一旦这些值可供上下文组件树中的所有组件使用,就可以使用 useContext 访问它们。该方法接受上下文对象并返回传递给 Provider 的值:

/context/component.jsx
import { createContext, useContext } from "solid-js";
import { MyContext } from "./create";
const Provider = (props) => (
<MyContext.Provider value="new value">{props.children}</MyContext.Provider>
);
const Child = () => {
const value = useContext(MyContext);
return <span>{value}</span>;
};
export const App = () => (
<Provider>
<Child />
</Provider>
);

自定义上下文工具函数

当应用程序包含多个上下文对象时,可能很难跟踪正在使用哪个上下文对象。要解决此问题,您可以创建自定义工具函数来创建更易读的方式访问上下文值。

例如,当您想要创建一个可用于包裹组件树的自定义 Provider 组件,你可以让其方便复用:

import { createSignal, createContext, useContext } from "solid-js";
import { CounterContext } from "~/context/counter";
export function CounterProvider(props) {
let count = 0;
return (
<CounterContext.Provider value={count}>
{props.children}
</CounterContext.Provider>
);
}

现在,如果您必须在应用程序的不同区域访问 Provider,您可以简单地导入 CounterProvider 组件并包裹组件树:

import { CounterProvider } from "./counterProvider";
export function App() {
return (
<CounterProvider count={1}>
<h1>Welcome to Counter</h1>
<NestedComponents />
</CounterProvider>
);
}

同样,您可以创建自定义工具函数来访问上下文值。创建自定义工具函数可以更轻松地访问您需要的值,而不是导入 useContext 并在您使用它的每个组件上传递上下文对象:

export function useCounter() {
return useContext(CounterContext);
}

这个例子中的 useCounter() 工具函数现在可以导入到需要访问上下文值的任何组件中:

import { useCounter } from "./counter";
export function CounterProvider(props) {
const count = useCounter();
return (
<>
<div>{count()}</div>
</>
);
}

Updating Context Values

信号提供了一种使用上下文同步和管理跨组件共享数据的方法。您可以将信号直接传递到 Provider 组件的 value 属性,对信号的任何更改都将反映在使用上下文的所有组件中。

这提供了一种管理组件状态的方法,而无须通过中间元素传递 props。


上下文调试

createContext 接收一个可选的默认值,如果未提供,它可能会返回 undefined。使用 TypeScript 时,这可能会导致类型问题,从而很难确定组件未按预期渲染的原因。

要解决此问题,可以在创建上下文对象时指定默认值,或者通过使用自定义 useMyContext 手动处理错误:

/context/counter-component.tsx
import { useContext } from "solid-js";
function useMyContext() {
const value = useContext(MyContext);
if (!value) {
throw new Error("Missing context Provider");
}
return value;
}
function Child() {
const value = useMyContext();
return <div>{value}</div>;
}

createContext 和 useContext 的常见问题

如果没有将默认值传递给 createContext ,则 useContext 可能会返回 undefined

因此,如果未将初始值传递给 createContext ,则 useContext 的 TS 类型签名将指示返回的值可能是 undefined (如上所述)。当您想要在组件内使用上下文时,尤其是立即解构上下文时,这可能会非常烦人。此外,如果您使用 useContext 并且它返回 undefined (这通常但并非总是错误的结果),运行时抛出的错误消息可能会令人困惑。

最常见的解决方案是将 useContext 的所有使用包裹在一个函数中,如果上下文是 undefined ,该函数将显式抛出有用的错误。这也可以收窄返回的类型,因此 TS 不会提示错误。举个例子:

/context/counter-component.tsx
function useCounterContext() {
const context = useContext(CounterContext);
if (!context) {
throw new Error("can't find CounterContext");
}
return context;
}
报告此页面问题