Reputation: 13
I am pretty new to development and need some help—and patience. I'm converting a React project to TypeScript and having some issues managing and updating the state inside my Context component.
I'm building a log viewer that displays a series of tasks and their nested sub-tasks, depending on which task is selected.
The events are defined in an Event class which is populated with data from a custom useFetch hook. These are stored as objects inside Event arrays. When a task is selected, it is stored in a currently 'selectedTask' state and must also be pushed into a 'Hierarchy' array.
The idea is to display a hierarchy of subsequently selected tasks, as the user clicks through multiple nested layers of task logs.
These events are defined inside a type 'ContextObject' as follows:
hierarchy: Event[] | undefined
setHierarchy: Dispatch<SetStateAction<Event[] | undefined>>
selectedSubEvent: Event[];
setSelectedSubEvent: Dispatch<SetStateAction<Event[]>>;
They are managed via useState:
const [hierarchy, setHierarchy] = useState<Event[] | undefined>([]);
const [selectedSubEvent, setSelectedSubEvent] = useState<Event[]>([]);
In order to update the hierarchy with the selected task, I'm using a state updating function inside a useEffect as follows, and getting an error on the '...prevState':
useEffect(() => {
setHierarchy((prevState) => [...prevState, selectedSubEvent])
}, [selectedSubEvent]);
The error I'm getting:
Type 'Event[] | undefined' is not an array type.ts(2461)
(parameter) prevState: Event[] | undefined
When I forgo the state update function inside the useEffect and just set 'Hierarchy' to the 'selectedEvent', it does set that single event to the hierarchy, but obviously I need more than that.
I've also tried checking the prevState before spreading it, but that gave multiple errors:
setHierarchy((prevState) =>
prevState ? [...prevState, selectedSubEvent] : [selectedSubEvent]
);
//Errors
Argument of type '(prevState: Event[] | undefined) => (Event | Event[])[]' is not assignable to parameter of type 'SetStateAction<Event[] | undefined>'.
Type '(prevState: Event[] | undefined) => (Event | Event[])[]' is not assignable to type '(prevState: Event[] | undefined) => Event[] | undefined'.
Type '(Event | Event[])[]' is not assignable to type 'Event[]'.
Type 'Event | Event[]' is not assignable to type 'Event'.
Type 'Event[]' is missing the following properties from type 'Event': key, id, startTime, endTime, and 3 more.ts(2345)
I realise I'm not providing much in the way of what I've tried, but I have tried countless articles, tutorials and both the React and TypeScript documentation and I'm at a loss 🤷♂️
Please let me know if I can provide any more info. I'm happy to keep digging if someone can just point me in the right direction.
I tried the suggestions offered by @Leigh.D and got the following results.
FIRST: Removing 'undefined' gives me the following errors:
Argument of type '(prevState: Event[]) => (Event | Event[])[]' is not assignable to parameter of type 'SetStateAction<Event[]>'.
Type '(prevState: Event[]) => (Event | Event[])[]' is not assignable to type '(prevState: Event[]) => Event[]'.
Type '(Event | Event[])[]' is not assignable to type 'Event[]'.
Type 'Event | Event[]' is not assignable to type 'Event'.
Type 'Event[]' is missing the following properties from type 'Event': key, id, startTime, endTime, and 3 more.ts(2345)
(parameter) prevState: Event[]
SECOND: This also results in an error on the setHierarchy property in my context provider value, but when I remove 'undefined' from setHierarchy in the context object like so:
setHierarchy: Dispatch<SetStateAction<Event[]>>
this error goes away:
Type 'Dispatch<SetStateAction<Event[]>>' is not assignable to type 'Dispatch<SetStateAction<Event[] | undefined>>'.
Type 'SetStateAction<Event[] | undefined>' is not assignable to type 'SetStateAction<Event[]>'.
Type 'undefined' is not assignable to type 'SetStateAction<Event[]>'.ts(2322)
sub-event-context.tsx(20, 2): The expected type comes from property 'setHierarchy' which is declared here on type 'SubEventContextObject'
(property) setHierarchy: React.Dispatch<React.SetStateAction<Event[] | undefined>>
THIRD: When I combine removing the 'undefined' as per FIRST and check the 'prevState' I get the following:
Argument of type '(prevState: Event[]) => (Event | Event[])[]' is not assignable to parameter of type 'SetStateAction<Event[]>'.
Type '(prevState: Event[]) => (Event | Event[])[]' is not assignable to type '(prevState: Event[]) => Event[]'.
Type '(Event | Event[])[]' is not assignable to type 'Event[]'.
Type 'Event | Event[]' is not assignable to type 'Event'.
Type 'Event[]' is missing the following properties from type 'Event': key, id, startTime, endTime, and 3 more.ts(2345)
(parameter) prevState: Event[]
FOURTH: When I remove the checked 'prevState' and typecast it, VSCode/Prettier wraps it in parenthesis—I don't know if this is normal behaviour?
useEffect(() => {
setHierarchy((prevState) => [
...(prevState as Event[]),
selectedSubEvent,
]);
}, [selectedSubEvent]);
That aside, I get the same errors as in THIRD.
Thank you to Leigh.D and Daniel Baldi for helping me resolve this. It appears I had TWO problems and their two answers collectively fixed my errors. Anyone reading this looking to resolve similar issues, be sure to read BOTH answers.
I'm marking Daniel's solution as the accepted answer, primarily because in my inexperienced opinion, it was the more complicated issue. Please correct me if my thinking is incorrect.
Thank you again for the help offered. I do appreciate it!
Deon
Upvotes: 1
Views: 3399
Reputation: 920
Ok, so I think I've figured out what's happening here. Let us consider what is going on here first:
setHierarchy((prevState) =>
prevState ? [...prevState, selectedSubEvent] : [selectedSubEvent]
);
As you've stated in your ContextObject
interface, selectedSubEvent
is an array of Event
and prevState
has the same type of hierarchy
and thus is an array of Event
or undefined.
So looking at the code, if prevState
evaluates to a truthy value, you're creating this [...prevState, selectedSubEvent]
, which contains a spread of prevState
, which results in Event
, and selectedSubEvent
, which results in an array of Event. This is the first problem, as the resulting value ends up being of type (Event | Event[])[]
when it should be of type Event[]
.
When prevState
is falsy, you're setting hierarchy
to be [selectedSubEvent]
. This creates an array of arrays of Event (Event[][]
). That's problem number two!
I might have gotten something wrong here as it is pretty tricky to try and write code without actually running it, but my guess is try this and it should be working fine:
setHierarchy((prevState) =>
prevState ? [...prevState, ...selectedSubEvent] : selectedSubEvent
);
Upvotes: 0
Reputation: 485
The problem is in your type definition (specifically undefined) and how the spread operator interprets it.
const [hierarchy, setHierarchy] = useState<Event[] | undefined>([]);
You can either remove undefined from the type (since you default the state to an empty array anyway), perform a check when setting state or typecast prevState so the spread operator knows its an array (not best option) ie:
setHierarchy((prevState) => prevState? [...prevState, selectedSubEvent] : [selectedSubEvent])
setHierarchy((prevState) => [...prevState as Event[], selectedSubEvent])
Upvotes: 1