正如您在示例中看到的,状态值类型是TrainsDetailsResponseType | undefined:
// typeof trainsDetails = TrainsDetailsResponseType | undefined
const [trainsDetails, setTrainsDetails] = useState<TrainsDetailsResponseType>();
您可以在 React 上下文值中为 trainsDetails 指定类似的类型:
interface TrainsContextValues {
trainsList: TrainsListResponseType[];
// optional type (?) or TrainsDetailsResponseType | undefined
trainsDetails?: TrainsDetailsResponseType;
handlePushToTrainsDetails: (arg: string) => void;
trainId: string;
}
export const TrainsContext = React.createContext<TrainsContextValues>({
trainsList: [],
trainsDetails: undefined,
handlePushToTrainsDetails: () => undefined,
trainId: "",
});
这是正确的,因为在您从端点获取之前,您确实没有 trainsDetails 的数据。
已更新。长答案
传递给createContext 的参数不是上下文的initialValue,而是defaultValue,这一点很重要。 defaultValue 参数仅在树中组件上方没有匹配的 Provider 时使用。
你有两种使用上下文的场景:
1. - 您允许使用上下文,直到收到所有必要的数据。在这种情况下,如果没有数据,您应该提供备用:
interface TrainsContextValues {
trainsList: TrainsListResponseType[];
trainsDetails?: TrainsDetailsResponseType;
handlePushToTrainsDetails: (arg: string) => void;
trainId: string;
}
export const TrainsContext = React.createContext<TrainsContextValues>({
trainsList: [],
trainsDetails: undefined,
handlePushToTrainsDetails: () => {},
trainId: "",
});
const Parent = () => {
const [trainsDetails, setTrainsDetails] =
useState<TrainsDetailsResponseType>();
useEffect(() => {
const getTrainsDetail = async () => {
try {
const response = await fetch("your-api");
const data: TrainsDetailsResponseType = await response.json();
setTrainsDetails(data);
} catch {
// your logic for the failed request
}
};
getTrainsDetail();
}, []);
const contextValue = useMemo(
() => ({
// undefined | data from api
trainsDetails,
// replace to actually
trainsList: [],
handlePushToTrainsDetails: () => {},
trainId: "",
}),
[trainsDetails]
);
return (
<TrainsContext.Provider value={contextValue}>
<Child />
</TrainsContext.Provider>
);
};
const Child = () => {
const { trainsDetails } = useContext(TrainsContext);
// technically, the component can be rendered out of context and it should be ready for this
if (!trainsDetails) {
return <FallbackComponent />;
}
return <TrainsDetailsComponent data={trainsDetails} />;
};
2. - 您不允许在没有真实数据的情况下使用上下文。 WARNING - 这是一个非常严格的模式,它仅在应用程序不能和不应该使用没有真实数据的上下文时使用:
interface TrainsContextValues {
trainsList: TrainsListResponseType[];
trainsDetails: TrainsDetailsResponseType;
handlePushToTrainsDetails: (arg: string) => void;
trainId: string;
}
export const TrainsContext = React.createContext<TrainsContextValues | null>(
null
);
const useTrainsContext = (): TrainsContextValues => {
const data = useContext(TrainsContext);
if (!data) {
throw new Error("could not find trains context value");
}
return data;
};
const Parent = () => {
const [trainsDetails, setTrainsDetails] =
useState<TrainsDetailsResponseType>();
useEffect(() => {
const getTrainsDetail = async () => {
try {
const response = await fetch("your-api");
const data: TrainsDetailsResponseType = await response.json();
setTrainsDetails(data);
} catch {
// your logic for the dropped request
}
};
getTrainsDetail();
}, []);
const contextValue = useMemo(
() =>
trainsDetails
? {
trainsDetails,
trainsList: [],
handlePushToTrainsDetails: () => {},
trainId: "",
}
: null,
[trainsDetails]
);
if (!contextValue) {
// some fallback for empty trainsDetails
return <Loading />;
}
return (
<TrainsContext.Provider value={contextValue}>
<Child />
</TrainsContext.Provider>
);
};
const Child = () => {
const { trainsDetails } = useTrainsContext();
return <TrainsDetailsComponent data={trainsDetails} />;
};
第二种情况是特定的,仅在上下文中缺少数据是组件组合中的逻辑错误时使用。并且用户无法纠正这种情况。例如,redux 中缺少 store 提供程序。