-2

I always run into situations where I need to force rerender, while I'm still in the execution of some function, so I developed my solution to this and I need to know if this is right or there is a simpler way to achieve the same goal.

I rely on the state variable my_force_update, then I change it to a random value when I want to enforce a change. like:

const [my_force_update, setMyForceUpdate] = useState(0);

useEffect(()=>{}, [my_force_update]);

const handleSubmit =  async () =>{
     await prm1();
     stMyForceUpdate(Math.random()); // enforcing the effect
     await prom2();
     ....
}

so I have been able to enforce re-render (by enforcing the effect) while I'm still in the handleSubmit execution.

is there a simpler way? or, did I mistakenly understand the concepts of React?

update

The issue is that I have a checkout form, and I need it to be a signup form at the same time, and there is also a login component on the page.

so I need to populate the form fields with the account if information in case of login and in case of sign up.

The steps are as follow:

if user login => populate form (per fill it with user info) => move to payment.

if user fill out the form manually:

  1. create an account.
  2. authenticate the new user.
  3. update the user account.
  4. repopulate form (with data from user account).
  5. move to payment.

so I have this function that needs to listen to the login and signup:

  const token = useSelector(_token);
  const loggedIn = useSelector(_loggedIn);
  const profile = useSelector(_profile);

  useEffect(() => {
    /**
     * Pre-fill the form inputs
    */
    (async () => {
      const r = await dispatch(fetchUserInfo());
      setFormProfile(profile); // address is not updated yet
      setFormAddress(r?.result?.address);
    })();
  }, [loggedIn, forceUpdate]);

now, there are no issues with the login process, the only problem is with the signup:

  1. at step 2, when authenticating the user, its account is empty.
  2. so the loggedIn changes to true when the profile is empty so I got empty form.
  3. after updating the profile, loggedIn will not change, so I need another variable to trigger the effect again.

I tried to listen to profile here, but I got an infinite loop.

and here is the checkout flow related to the signup:

...

 if (!loggedIn) {
      const signupResponse = await dispatch(signupUser(params));
      loginResponse = await dispatch(login(formProfile?.email, password));

    }

   const updateProfileResponse =  await saveChangesToProfile(); 
// update user profile with the information in the checkout form.

...


then save changes to the profile:


  const saveChangesToProfile = async () => {
    const r = await dispatch(fetchUserInfo());
    const addressID = r?.result?.address_id;

    const res1 = await dispatch(updateUserAddress(addressID, { ID: addressID, ...formAddress }));

    const res = await dispatch(UpdateUser(r?.result?.ID, formProfile));

    setForceUpdate(Math.random()); // force re-render to re-populate the form. 

    setSuccess("Information saved to your profile!");

    return res;
  };

Update 2

The question is general, I solved the issue in another way days ago (involving changes to the server routes). and I'm asking the question in a general way to get some knowledge, not for others to do the work for me.

Ahmad Ali
  • 704
  • 1
  • 10
  • 28
  • 3
    explain why you need rerender what are you achieving doing it? so we understand it better – Sarkar Jul 17 '21 at 08:29
  • 1
    The [React Docs](https://reactjs.org/docs/hooks-faq.html#is-there-something-like-forceupdate) have a recommended way of handling this. It's almost certainly unnecessary in 99% of cases, you should not be reaching for it that often. – lawrence-witt Jul 17 '21 at 08:41
  • 3
    I can tell you from half an internship experience trying to learn React coming from an OOP background, if you feel you are fighting React and needing to force update to rerender you aren't React-ing correctly. Learn the React component lifecycle and use it to your advantage, don't work against it, it will be nothing but struggle. – Drew Reese Jul 17 '21 at 08:47
  • Better to post full code, most of the times force updates is a bad idea. – Aleksandr Smyshliaev Jul 17 '21 at 09:21
  • I have updated the question with more info, please consider reading it again. – Ahmad Ali Jul 17 '21 at 10:30
  • Could you please provide a [codesandbox](https://codesandbox.io/s/react-new) with your example? – johannchopin Jul 26 '21 at 08:08
  • If you've things that are dependant on user login then you should update those things whenever the user login happen instead reloading the entire view and catch the new values due to the refresh. Where did you store the current state? why you can't use the state to handle this? – JoelBonetR Jul 26 '21 at 12:35
  • @JoelBonetR is that those values are stored in Redux, and I'm not super familiar with redux, but somehow values are updated in the state( or redux) while the other part (state/redux) is still not notified about the change. – Ahmad Ali Jul 26 '21 at 13:06
  • then I'd suggest you to learn how it works and refactor your code instead patching things on a bad manner – JoelBonetR Jul 26 '21 at 13:08
  • @JoelBonetR the question is general, I solved the issue in another way days ago. and I'm asking the question in a general way to get some knowledge, not for others to do the work for me. – Ahmad Ali Jul 26 '21 at 13:12
  • I would not mean that, I would mean that you can learn how to use redux to, frst of all analyse and ensure that you need redux (you can use react own state management, mobX or your own implementation instead, depending on the project needs) and then you'll probably avoid doing full re-paint on your views. – JoelBonetR Jul 26 '21 at 13:15

1 Answers1

2

In general, you should avoid having to force an update in React but instead use existing React features to accomplish your goal. That being said, there are simple ways to force a re-render in react. You mentioned in the second update that you are looking for more general solutions - so I will provide them here. However, please bear in mind that this topic has been discussed extensively in other stack overflow questions (I will provide links).

Forcing Re-Render using component.forceUpdate(callback)

The react docs actually list a simple way to force a component to reload (provided you maintain a reference to it). You can find more information here, but essentially it forces your component to re-render and then makes a call to the callback argument.

Forcing Re-Render using hooks

There are multiple stack overflow questions that provide simple code snipets that can force a react component to re-render by using hooks. This answer for example by @Qwerty demonstrates 2 simple code snipets to force a re-render:

const forceUpdate = React.useState()[1].bind(null, {})  // see NOTE above
const forceUpdate = React.useReducer(() => ({}))[1]

You should check out his answer for a more detailed explanation.

Other sources include this answer to the same stack overflow question that references the official FAQ. It solves the problem by doing:

const [ignored, forceUpdate] = useReducer(x => x + 1, 0);

Solving Your Specific Problem

I saw that you were able to solve your problem by using the useEffect hook - a great start for a potential solution. You also mentioned that you got an infinite loop while listening to a variable change in your hook - a common problem and one with some common solutions. In general, you should always run a check inside the useEffect hook before changing any of its dependencies. For example, run a check to see if the profile is unset before trying to update its value.

I however would recomend that you use a progress varible that would indicate your status, something like this:

const STATUS_START = 0;
const STATUS_LOGED_IN = 1;
const STATUS_SIGNING_UP = 2;
const [progress, setProgress] = useState(STATUS_START);

Then, you can simply listen to changes made to the progress variable in your useEffect hook (by passing it as your only dependent). This should automatically condition you to write the necessary logic to check for state inside of the useEffect function as I described previously.

This solution would work by initially setting the progress to either signing up or logging in, but only filling the form data if you are logged in (and after the signup progress is done calling setProgress(STATUS_LOGED_IN))

Geo
  • 543
  • 5
  • 16