0

I am trying to implement Facebook login to my React app.

const [formData, setFormData] = useState({
    name: "",
    surname: "",
    email: "",
    password: "",
  });

Facebook login here

<FacebookLogin
 appId=""
 autoLoad={true}
 fields="first_name,last_name,email,id"
 onClick={(e) => componentClicked(e)}
 callback={(response) => responseFacebook(response)}
 textButton={msg}
 cssClass="btn btn-primary btn-sm"
 icon="fa-facebook"
 />

The data is coming to:

const responseFacebook = (response) => {
    setFormData({ ...formData, name: response.first_name });
    setFormData({ ...formData, surname: response.last_name });
    setFormData({ ...formData, email: response.email });
    setFormData({ ...formData, password: response.id });
    console.log(formData.name);
    console.log(formData.surname);
    /* register({ name, surname, email, password }); */
  };

I can't set formData.x. response.first_name or others are working with console.log, but when I try to set the state, it turns nothing.

eg, console.log(formData.name) returns nothing.

Vencovsky
  • 28,550
  • 17
  • 109
  • 176
  • use setState callback function too see change –  May 07 '20 at 20:21
  • Does this answer your question? [useState set method not reflecting change immediately](https://stackoverflow.com/questions/54069253/usestate-set-method-not-reflecting-change-immediately) – Code-Apprentice May 07 '20 at 20:25

4 Answers4

2

You should use the previous state or call only one time setFormData.

// using previous state
// this solves your problem but isn't that good
// because you don't need to call it many times
const responseFacebook = (response) => {
    setFormData(prev => ({ ...prev, name: response.first_name });
    setFormData(prev => ({ ...prev, surname: response.last_name });
    setFormData(prev => ({ ...prev, email: response.email });
    setFormData(prev => ({ ...prev, password: response.id });
};

OR

// calling only once
const responseFacebook = (response) => {
    setFormData({ 
         name: response.first_name,
         surname: response.last_name,
         email: response.email,
         password: response.id 
    });
};
Vencovsky
  • 28,550
  • 17
  • 109
  • 176
2
setFormData({ ...formData, name: response.first_name });
setFormData({ ...formData, surname: response.last_name });
setFormData({ ...formData, email: response.email });
setFormData({ ...formData, password: response.id });

The problem is that formData does not change until the next render. The value of formData is the value on the previous render, and it stays that way until the next render. So each of the above lines, sets the old data, plus one changed property. Then the next lines sets the old data with a different changed property, and the property you changed before that is lost.

You just want to call setFormData once, with all the changes you want. That way you don't need formData to update at all until after you're done setting everything.

setFormData({
  ...formData,
  name: response.first_name,
  surname: response.last_name,
  email: response.email,
  password: response.id,
});

But looking at your the structure of your state here, you don't need the previous values at all since every key is in the response:

setFormData({
  name: response.first_name,
  surname: response.last_name,
  email: response.email,
  password: response.id,
});
Alex Wayne
  • 178,991
  • 47
  • 309
  • 337
  • This is a bad practice. We shall use the setState callback when merging data to state. Otherwise, you could have a stale state. – Ian Vasco May 07 '20 at 20:27
  • @IanVasco You don't always need to use the callback. It's a useful tool, but it depends on how data flows around the application. But, in this case the previous state isn't even necessary at all. – Alex Wayne May 07 '20 at 20:36
  • I agree that for this specific use case is not needed. But still, I think that's a good practice to use to reduce side effects – Ian Vasco May 07 '20 at 20:43
1

use usestate to set state and useeffect hook to console change , your state doesnt change because its an asynchronous task , in hooks there no callback function in setting state like class , so use useEffect

const responseFacebook = (response) => {
    setFormData({ 
      ...formData, 
      name: response.first_name 
      surname: response.last_name,
      email: response.email,
      password: response.id 
    });
});

useEffect(() => {
   console.log(formData.name);
    console.log(formData.surname);
}, [formData]);
0

useState Hooks are asynchronous, you will not be able to see the data reflected immediately, instead use useEffect to see your result

const responseFacebook = (response) => {
    setFormData({ ...formData, name: response.first_name });
    setFormData({ ...formData, surname: response.last_name });
    setFormData({ ...formData, email: response.email });
    setFormData({ ...formData, password: response.id });
    console.log(formData.name); // This won't show the results
    console.log(formData.surname); // This won't show the results
    /* register({ name, surname, email, password }); */ // This won't show the results
  };

useEffect(() => {
    // action on update of formData
 console.log(formData);
}, [formData]);

Also you could further optimise your code to:

   setFormData({ 
    ...formData,
    name: response.first_name,
    surname: response.last_name,
    email: response.email,
    password: response.id 
   });

useEffect(() => {
    // action on update of formData
 console.log(formData);
}, [formData]);
Ayushya
  • 1,862
  • 1
  • 10
  • 15