FAQ: The State Hook - Lesson Review

I spotted three things which need to be corrected.

  • The first mistake is a missing opening parenthesis within your handleChange event handler:
// You wrote:
setNewTask((prevState) => {
// It should be:
setNewTask((prevState) => ({

It has to do with implicit return of an object. See this post for why we need to wrap the curly braces in parentheses (the explanatory text of the “Objects in State” exercise also makes a brief mention of this). Your code does have the closing parenthesis though i.e. the line with })); , so you are only missing the opening parenthesis.

  • The second mistake is an extra closing parenthesis within your handleSubmit event handler:
// You wrote:
setAllTasks((prevState) => [newTask, ...prevState]));
// It should be:
setAllTasks((prevState) => [newTask, ...prevState]);

Here, we are doing an implicit return of an array. Since an array uses brackets [ ] instead of curly braces { }, so we can do an implicit return of the array without the need of parentheses.

  • The third mistake is a missing opening parenthesis within your handleDelete event handler:
// You wrote:
setAllTasks((prevState) => prevState.filter(task) => task.id !== taskIDtoRemove));
// It should be:
setAllTasks((prevState) => prevState.filter((task) => task.id !== taskIDtoRemove));

You are missing an opening parenthesis after filter. You do have the closing parenthesis though, so you only need to insert the opening parenthesis.

Thank you very much!

Agreed, I don’t see the point of taking this course if it offers virtually no guidance on what to do. “Here’s a bunch of code you don’t understand and barely remember, have fun rewriting it”.

4 Likes

This exercice was very tough and I spent moons to debug my code just because of syntax problems…

I used curly braces in my arrow functions inside of the setNewTask function, like that :

newTask => {
({ …newTask, [name]: value, id: Date.now() })
}

Only because of that, my screen became completely blank after typing some text in the input!

Later on, I realized that it has to be in one single line without curly braces as per below:

newTask => ({ …newTask, [name]: value, id: Date.now() })

This kind of syntax error is so hard to debug!
I still cannot figure out why React doesn’t allow me to write arrow functions with curly braces in this context.

In the lesson “Arrays in State”, there is an example with a function called toggleTopping, and inside this function we called setSelected and in this arrow function we used curly braces like that: setSelected((prev) => {
In this example there is a conditional statement so we must use curly braces.

So why in this context we cannot use curly braces ?

Maybe it’s a bit stupid to focus on syntax, but I am frustrated, and I didn’t manage to use React Developers Tool to help me find the error!

It has to do with implicit/explicit returns. If the body of the function has more than one statement, then it is necessary to use curly braces. When the body of the function is just one expression, then we have a choice. The two choices are:

  • Choice A: We can enclose the expression in curly braces. But, if the function is meant to return some value, then we must do an explicit return by using the return keyword. If we omit the return keyword, then the function’s return will be undefined.

  • Choice B: We can choose to omit the curly braces. Whatever the expression evaluates to will be returned implicitly. Trying to return explicitly using the return keyword is not allowed and will cause error to be thrown.

Either we go with Choice A or with Choice B. What we aren’t allowed to do is mix the two approaches. If we go with Choice A (wrap in curly braces) and wish to return something, then we can’t omit the return keyword. If we go with Choice B (omit the curly braces), then we can’t do an explicit return by using the return keyword.

// Explicit Return 
const myFunc1 = (number) => {
    return number + 5;
}

// Implicit Return
const myFunc2 = (number) => number + 5;

// Curly Braces without explicit return will cause undefined return
const myFunc3 = (number) => {
    number + 5;
}

// No Curly Braces and explicit return will cause SyntaxError
const myFunc4 = (number) => return number + 5;

a = myFunc1(10);   // a will be 15
b = myFunc2(10);   // b will be 15
c = myFunc3(10);   // c will be undefined
d = myFunc4(10);   // SyntaxError

There is a twist though. Suppose we want to do an implicit return of an object. We may try to do something like:

/// Curly brace of object mistaken for curly brace of function body

const myFunc = () => { cars: 4, bikes: 7}

a = myFunc();  // SyntaxError

const myFunc = () => { cars: 4 }

a = myFunc();  // undefined

Since Javascript looks at the curly braces in this context as designating the body of a function, so we can’t just use curly braces. We have to wrap them in parentheses so that Javascript knows that this is meant to be an object and not the body of the arrow function. Have a look at this article. It is a short read, but it explains the issue very nicely with examples.

// Implicit Return of an object
const myFunc = () => ({cars: 4, bikes: 7})

a = myFunc();  // a will be {cars: 4, bikes: 7}

We can also do an explicit return, but we must use the return keyword if we want to return some value.

// Explicit Return of an object
const myFunc = () => { 
    return {cars: 4, bikes: 7};
}
a = myFunc();  // a will be { cars: 4, bikes: 7}

In the exercises you mentioned, we are using arrow functions in state setters. State setters expect that some value will be returned to them so that they can replace the previous state with the new state. As long as you respect the syntax of implicit/explicit returns, both approaches can be used.

1 Like

Thank you for the great explanations! It makes it very clear now :smiley:

1 Like
{!newTask.title ? null : (
        <>
          <textarea
            name="description"
            placeholder="Details..."
            value={newTask.description || ""}
            onChange={handleChange}
          />
          <button type="submit">Add Task</button>
        </>
)}

I wonder how this works?
Why here is a empty <> and I know nothing about this syntax.

Revisit the earlier lesson JSX Outer Elements.

…a JSX expression must have exactly one outermost element…

In the snippet you posted, there are two elements textarea and button. Only one outer element is allowed, so we can wrap them in say a <div> ... </div>, but if we don’t want to add extra nodes to the DOM by creating divs, we can instead wrap the elements in a fragment.

Have a look at the documentation:

In the snippet posted by you, the short syntax (mentioned in above link) is used.

Wow, this syntax helps me a lot!
I often wonder if there is a better way instead add <div> when I practice React projects.
I wish I could know <React.Fragment> earlier.

1 Like

Hello,
To better understand what newTask and allTasks represent, I have a question to ask you:

If I understood correctly, newTask is an object that stores, in a consecutive way, all the name and id properties of all the tasks, is it really that?
Is it possible to represent it like this:
{ [name] : valueTask1 ,
id:idTAsk1,
[name] : valueTask2,
id:idTAsk2,
[name] : valueTask3,
id:idTAsk3,
[name] : valueTask4,
id:idTAsk4, etc …}
Is this correct?

And, if I still understand correctly, allTasks represents an array that contains objects that represent all tasks.
Can we imagine it like that?
[{task1},{task2},{task3}, etc…] and looking at it in more detail would that be it? :

[{[name]: valueTask1, id:idTAsk1},{[name]: valueTask2, id:idTAsk2}, {[name]: valueTask3, id:idTAsk3},{[name]: valueTask4, id:idTAsk4,} etc …]

Thanks in advance for your answers

Translated with DeepL Translate: The world's most accurate translator (free version)

newTask is an object and allTasks is an array.

The form displayed on screen consists of an input element for the title of the task, a button for submitting the task and (once we type in a title), a textarea element for entering Details of the task also appears.

newTask starts off being an empty object and initially allTasks is an empty array.

// Initially
newTask ---> {}
allTasks ---> []

As we type in text for the title or for the details, the properties and associated values of the new task are created or updated (depending on how the user is interacting with the form). Once the task is submitted, this new task object is added to the allTasks array. The newTask object is then reset to an empty object.

If you look at the file NewTask.js (you can access it by clicking on the Folder Icon in the top left corner of the editor and then navigate to the Presentational folder), you will see that the HTML input element has been given the attribute name="title" and the textarea element has been given the attribute name="description"

In the setters, we are using spread syntax (to preserve existing properties) and computed property names (see documentation: Object initializer - JavaScript | MDN ) to provide the appropriate properties,

//EXAMPLE - Computed Property Names
let name = "title";

// This in not what we want
let x = {name: "Some text"};
console.log(x); // {name: "Some text"}

// This is what we want
let y = {[name]: "Some text"};
console.log(y); // {title: "Some text"}

Suppose we type in the text "First Task" in the title input box. The event handler handleChange will be triggered and result in

// xxxxx is time in milliseconds when event handler was triggered
newTask ---> { title: "First Task", id: xxxxx }
allTasks ---> []

Suppose we type in text "Complete this task soon" in the Details textarea, then handleChange will be triggered and result in

// yyyyy is time in ms when event handler was triggered
// The spread syntax ... in the setter allows the previous title property to be retained
newTask ---> { title: "First Task", description: "Complete this task soon" , id: yyyyy }
allTasks ---> []

When the "Add Task" button is clicked, the handleSubmit event handler does the following:

newTask ---> {}
allTasks ---> [{ title: "First Task", description: "Complete this task soon" , id: yyyyy}]

If another new task is entered in the form, then:

// While entering task in form
newTask ---> { title: "Second Task", description: "Not so important" , id: zzzzz}
allTasks ---> [{ title: "First Task", description: "Complete this task soon" , id: yyyyy}]

// After submitting
newTask ---> {}
allTasks ---> [ { title: "Second Task", description: "Not so important" , id: zzzzz}, 
              { title: "First Task", description: "Complete this task soon" , id: yyyyy} ]
1 Like

Literally man. It demotivates me so much when I get through a bunch of lessons and then am thrown so far into the deepn end that I have absolutely no clue what is going on. :frowning_face:

1 Like

agreed guys… I’m at least happy that you guys feel the same as me. I was just looking at my screen like how the ■■■■ am I meant to be able to do this all of a sudden? it’s super demotivating when they instruct stuff as if you should know what it means by now and you literally have no clue what is going on

1 Like

Hello,
Why is better to copy an entire object with the spread operator than modify the object itself?
Couldn’t lead to performance issues?

Hi! So I went through this course 5+ years ago when they were teaching class components. I’ve built my website around that; however, I’m finding less and less support around that method and most is around the functional components methods. I went through the funcational components lessons and the Hooks lessons, so that I can convert my website to this newer way, but I’m still not finding a clear answer to what I’m looking for.

I have many of my state setters call back a function to check after setting those specific states to do something with that (not at initial render, only when the state is set). Or just console log the specific state I changed, just to check things. My understanding is that useEffect also is performed at initial render which the setState callback function doesn’t do. I also have callback that won’t update a state until another state is updated first and this was really easy to do using the setState callback. But useEffect can not be nested to ensure that states are updated in a certain order like I was able to do with the setState callback.

What’s the solution translating this older way to functional? What lesson may cover this?
This is an example, of what I need. How do I do this to behave the same manner in functional components? I don’t want it to do anything upon render, just after the state is updated.

setState({ power:value }, ()=> { 
    console.log(this.state.power);
    const energy = this.state.power * this.state.time
    setState({energy}, ()=> { 
        this.validate()
    })
})