I have been trying some days to figure out how to update my global state array in a gis-project. I use Mapbox-gl with ts and react. I use the useContext hook to keep track of a list of "GeoJSONItems", which includes a geoJSON object and some metadata. However, several ts problems occur when I try to update the global array.your text
This is my GeoJSONContext.tsx
import { createContext, useContext, useState } from "react";
import { FeatureCollection } from "geojson";
export type GeoJSONItem = {
id: string,
name: string,
visable: boolean,
color: string,
geoJSON: FeatureCollection;
};
type GeoJSONContextType = {
geoJSONList: Array<GeoJSONItem>;
setGeoJSONList: (selected: GeoJSONItem[]) => void;
setVisable: (value: boolean) => void;
};
const GeoJSONContext = createContext<GeoJSONContextType>({
geoJSONList: [],
setGeoJSONList: () => {},
setVisable: () => {}
});
export const useGeoJSONContext = () => useContext(GeoJSONContext);
type Props = {
children?: React.ReactNode;
};
const GeoJSONProvider: React.FC<Props> = ({ children }) => {
const [geoJSONList, setGeoJSONList] = useState<GeoJSONItem[]>([]);
const [visable, setVisable] = useState<boolean>();
return (
<GeoJSONContext.Provider value={{ geoJSONList, setGeoJSONList, setVisable }}>
{children}
</GeoJSONContext.Provider>
);
};
export default GeoJSONProvider;
This is how global states should be handled with useContext (Works for local arrays) and how I try in my components:
setGeoJSONs((prevGeoJSONs) => [...prevGeoJSONs, json as FeatureCollection]);
I have managed to work it out with Array.push(), but I know its not a good practice working with global states.
So, when calling the global setGeoJSONList, when adding an Object or updating it:
setGeoJSONList((prevGeoJSONs) => [...prevGeoJSONs, json as FeatureCollection]);
ts sends this error: Argument of type '(prevGeoJSONs: any) => any[]' is not assignable to parameter of type 'GeoJSONItem[]' As if it dosen't understand that prevGeoJSONs is an array of GEOJSONItem. Note:
Hope somebody have time to help me 😃
You've declared setGeoJSONList
as type (selected: GeoJSONItem[]) => void
- in other words, it only takes an actual array of items.
However, you're trying to use it by passing it a function that produces an array of items:
setGeoJSONList((prevGeoJSONs) => [...prevGeoJSONs, json as FeatureCollection]);
The setter that's returned by useState<GeoJSONItem[]>
can take either a value or a function that produces a value; in other words, it's of type (selected: GeoJSONItem[] | ((prevSelected: GeoJSONItem[]) => GeoJSONItem[])) => void
. TypeScript understands this as long as you're using useState
's return value directly, but when you then pass it to a context property that's explicitly typed as (selected: GeoJSONItem[]) => void
, it requires only the array format.
Changing the type of your producer function should fix this.
See this answer.