如何在 React CreateContext 中正确键入对象初始值
How to type properly object initial value in React CreateContext
我应该如何在 React Create Context 中正确输入 constance trainsDetails 的初始值,这是一个具有不同属性的对象。 TrainsDetails 是从端点获取的单个对象,其值在下面以 TrainsDetailsResponseType 类型提供:
type TrainsDetailsResponseType = {
trainId: string;
trainNo: string;
lineId: string;
route: string;
lineKmPosition: string;
speed: number;
deviationFromTimetable: number;
mileage: number;
lastInspectionDate: string;
nextInspectionDate: string;
state: string;
};
这是 TrainsContextValues 的接口:
interface TrainsContextValues {
trainsList: TrainsListResponseType[];
trainsDetails: TrainsDetailsResponseType;
handlePushToTrainsDetails: (arg: string) => void;
trainId: string;
}
我应该如何正确输入 trainsDetails 的初始值?
export const TrainsContext = createContext<TrainsContextValues>({
trainsList: [],
trainsDetails: ,
handlePushToTrainsDetails: () => undefined,
trainId: '',
});
在这里您还可以看到我如何在 useState 挂钩中输入 trainsDetails
const [trainsDetails, setTrainsDetails] = useState<TrainsDetailsResponseType>();
如您在示例中所见,状态值类型为 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
参数仅在组件在树中没有其上方的匹配提供者时使用。
您有两种使用上下文的场景:
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 中缺少商店提供者。
我应该如何在 React Create Context 中正确输入 constance trainsDetails 的初始值,这是一个具有不同属性的对象。 TrainsDetails 是从端点获取的单个对象,其值在下面以 TrainsDetailsResponseType 类型提供:
type TrainsDetailsResponseType = {
trainId: string;
trainNo: string;
lineId: string;
route: string;
lineKmPosition: string;
speed: number;
deviationFromTimetable: number;
mileage: number;
lastInspectionDate: string;
nextInspectionDate: string;
state: string;
};
这是 TrainsContextValues 的接口:
interface TrainsContextValues {
trainsList: TrainsListResponseType[];
trainsDetails: TrainsDetailsResponseType;
handlePushToTrainsDetails: (arg: string) => void;
trainId: string;
}
我应该如何正确输入 trainsDetails 的初始值?
export const TrainsContext = createContext<TrainsContextValues>({
trainsList: [],
trainsDetails: ,
handlePushToTrainsDetails: () => undefined,
trainId: '',
});
在这里您还可以看到我如何在 useState 挂钩中输入 trainsDetails
const [trainsDetails, setTrainsDetails] = useState<TrainsDetailsResponseType>();
如您在示例中所见,状态值类型为 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
参数仅在组件在树中没有其上方的匹配提供者时使用。
您有两种使用上下文的场景:
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 中缺少商店提供者。