A spread argument must either have a tuple type or be passed to a rest parameter React
Asked Answered
L

6

82

I have this code in Typescript:

const [history, setHistory] = useState([Array(9).fill(null)]);
const newHistory = history.slice(0, currentStep + 1);

and when I want to set new State using spread operators like that:

setHistory(...newHistory);

I have errors:

A spread argument must either have a tuple type or be passed to a rest parameter.

Can someone can help my, how I can properly types this?

Lignocellulose answered 13/8, 2021 at 8:2 Comment(1)
this is the solution that worked for meCommodious
B
45

To put this simply:

Because you're using the spread operator directly, you're passing in multiple arguments to your useState function, but useState functions only accept a single argument. Instead, use the spread operator to create a single array, and pass that in.

const newObject = [...newHistory];
setHistory(newObject);

// or, even simpler: 
setHistory([...newHistory]);

// NOT the below - this is passing in multiple arguments.
setHistory(...newHistory);
Brenda answered 4/11, 2021 at 15:5 Comment(2)
huh, why an object?Urology
Oops - forgot the OP was dealing with an array rather than an object. (It works for objects, too.) Thanks, @Sulthan, good catch!Brenda
O
41

In general, you can avoid such errors by using apply

setHistory(...newHistory); //drops error

setHistory.apply(null, newHistory) //works

but in your example there's a problem: you init history with [[null, null, null...]] then get it, slice to [[null, null, ...], ...] and try to set it back as setHistory([null, null], null).

it seems that you just need to use setHistory(newHistory);

Oquassa answered 29/10, 2021 at 16:4 Comment(3)
What about calling super() in a class constructor? You can't use apply thenBreezy
I have no idea :), I come to this question by searching error that occurred in ts project without react, and found a general answer. So may be you can provide simpler solution in react context, which is not provided by the author.Oquassa
Recommending to avoid errors by using things that rely on any instead of actually addressing the root cause (which you did only in the second half of your answer) is not a good idea in my opinion.Loy
L
25

This worked for me perfectly!

setHistory(...(newHistory as []));
Lactometer answered 31/5, 2022 at 14:52 Comment(2)
This indeed makes the Typescript error go away - but would be incorrect for this question which wants you to pass an array to setHistory not function parametersCaseycash
this worked in my scenario! not the same as OP. I had a class that was extending another class and had to pass the rest of the arguments from my constructor to it's super() method.Gasometer
T
3

I had a similar issue as I'm new to React and Js, trying to spread an object, using useState to set the new value of the object using it's previous value.

const onChange = ({name, value}: OnChangeFormType) => {
  setForm(prev => {...prev, [name]: value});
};

However I didn't account for the fact that it's a function, and it should have been either wrapped in () or add a return. Personally I prefer just wrapping in () like below.

const onChange = ({name, value}: OnChangeFormType) => {
  setForm((prev) => ({ ...prev, [name]: value }));
};

or

const onChange = ({name, value}: OnChangeFormType) => {
  setForm(prev => { return {...prev, [name]: value}});
};

This was a head scratcher for me, until it clicked.

Telangiectasis answered 4/3, 2023 at 8:7 Comment(0)
U
1

When using useState, index 1 in the tuple returned is setHistory. That should only accept one argument (the new value). The example in the original post is spreading the history into potentially 8 arguments. It needs to be passed as an array which can be as simple as setHistory(newHistory).

If the intention is to always modify the previous state, I would call setHistory(previousHistory => previousHistory.slice(x, y)).

Also! For Typescript, you need to declare a type for useState like this useState<History[]>(Array(9).fill(null)); Without declaring a type, typescript is going to infer something that, in this case, may not be what you want.

Just note, you don't always need to declare a type for useState. Actually, I'd encourage using type inference whenever possible. If the initial value is null or undefined, you won't be able to use type inference in Typescript.

Unbacked answered 9/9, 2021 at 19:58 Comment(0)
S
-2

It can be resolved by assign the value to variable.


const latestHistory = {...newHistory}

setHistory(latestHistory);

Salvidor answered 17/11, 2022 at 1:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.