React ToDoApp with Functional Components - useState()

Hi, I’m trying to pass from the class syntax to the functional component syntax in React. I read the documentation about useState() and I’m trying to apply it on a simple ToDoApp (or something similar to an app which make posts).

I would like to know, if the code is well written until now. Until now I mean that, I’m only updating the initial state actually, but I had a lot of issues doing that. Any tipps about this basics?

And, another question is: to check the state I call specific state keys at line 61, 62, and 63, but before that, I was trying to check it in the main update function (called addPost), through a console.log(postObj), but it was always in late. I mean, when I write and submit the first time, the postObj is not updated; the second time, is updated, but with the data of the first time; the third time, is updated with the data of the second time etc. Is it related with asynchronism?

No, the code you’ve written works fine. You’re logging in the input handler, so you’re logging before the update happens. There’s nothing unexpectedly async happening here, it’s logging before the App function runs, so before the state updates.

So when the function App runs, it returns JSX, which is converted so some functions that generate HTML, thence rendering the stuff in the browser. Then you type some stuff; every time you type anything, either the getName or the getMessage functions run. Every time they run, the state updates. When you hit the post button, the addPost function runs. When that runs, the state updates.

Every time the state updates, App runs. App returns the JSX, which is converted so some functions that generate HTML. Before App runs, the state is still at the previous point, that’s what you’re logging.

Also, look:

const [postObj, setPost] = useState({
    currentName: "currName",
    currentMessage: "currMsg",
    postCollection: [
      {
        number: 0,
        name: "initName",
        message: "initMess"
      }
    ]
});

The getName and getMessage handlers, you fire those on the input change; they update as you type. But then, when you hit “Post it!”, you do this:

const addPost = () => {
    setPost((prevState) => {
      return {
        ...prevState,
        postCollection: {
          number: 1,
          name: prevState.currentName,
          message: prevState.currentMessage
        }
      };
    });
};

You’ve changed what postCollection is. I also assume that you want to increment the id number, not have every post as number 1.

1 Like

Yes exactly, this will be the next step, for this moment I was trying to really understand what’s happen with the functional components, because until now I’ve used only class syntax, but I read on the documentation that is like deprecated and the hooks are now the trend.

The biggest difference, I notice, is that the state is not automatically merged, right? That’s why I’m using ...prevState.

It’s not deprecated, just hooks are as a rule of thumb easier to understand and use (with some subtleties), so everyone just uses them instead.

Note it’s working the same as the state object (and the setState method) in this case, so in the case you’re talking about you’re overwriting the state with a different type of thing, which doesn’t make a lot of sense (you start with an array of objects, then overwrite that with one object).

Re how you’ve structured things, exactly the same issues as you would have with a class component.

You’ve put everything in a single god object, and it doesn’t quite make sense. In particular, I get you want to show the thing that’s been typed before committing to adding a message, but that’s already in the inputs, all you’re doing is repeating that. Just use multiple useState hooks to track individual values (there is useReducer to deal with objects, but this is simple enough to not require that)

So in pseudocode, you could do something like following. Three functions. State in parent is just the list of messages + a handler function to insert new messages into that array. The form child needs its own state (for the inputs, though you don’t need it, can use refs), and needs to be passed the handler function as a prop. The list of posts child only needs to be passed the list of messages as a prop.

# function: parent
set messages to initially be an empty array

create handler function saveMessage(title, message)  which:
  concatentates { title, message } to current messages array
  set messages to this new value

return JSX:
  <MESSAGE_FORM saveMessageHandler={saveMessage}>
  <LIST_OF_MESSAGES messages={messages}>
# function: message form
set title to initially be an empty string
set message to initially be an empty string

return JSX:
  <FORM onSubmit={saveMessage(title, message)}
    <TITLE_INPUT onChange={setTitle} value={title}>
    <MESSAGE_INPUT onChange={setMessage} value={message}>
    <BUTTON type="submit">Post It!
# function: list of messages
return JSX:
  <UL>
    map messages to <LI>{title}: {message}

I mean, it doesn’t have to be in separate components, it can all be in one, but keeping all the state in a single object is going to make things difficult to work with

1 Like