2

I'm dipping my toes in the React pool and can't seem to get my head round one aspect mainly around the amount of times the function renders the page.

Using the below example when the form validation fails I see the toast display TWICE.

The toast I am using is react-hot-toast

My understanding is that;

  • The page first renders and formErrors from the useState() is empty.

  • When the submit button is pressed it calls the onSubmit function and if the form fails validation it fires setFormErrors function

  • This causes a re-render of the page, while also populating the formErrors variable

  • As formErrors is not falsy etc it then fires the toast function.

But when I check the console I see it has fired toast twice and I see two toasts, this means it's rendered twice? Why is this?

Am I going about this all the wrong way? Is there a better method of using a function like toast to display messages? Should I be putting it all in a context (might not be right terminology) ?

export default function RegistrationPage() {
    const [formErrors, setFormErrors] = useState({})
  
    const passwordField = useRef()   
    const password2Field = useRef()

    useEffect(()=>{
        usernameField.current.focus()
    })

    const onSubmit = (ev) =>{
        ev.preventDefault()
        if(passwordField.current.value !== password2Field.current.value){
            setFormErrors({"password2": "Passwords do not match"})
        }else{
           console.log("Need to implement")
        }
    }

    if(formErrors && Object.keys(formErrors).length > 0){
        toast('Here is your toast.');
        console.log("Toast Fired");
    }

    return (
        <Body>
            <h1>Login</h1>
            <Form onSubmit={onSubmit}>
                <InputField fieldRef={usernameField} name="username" label="Username" error={formErrors?.username}></InputField>
                <InputField fieldRef={emailField} name="email" label="Email Address" type="email" error={formErrors?.email}></InputField>
                <InputField fieldRef={passwordField} name="password" label="password" type="password" error={formErrors?.password}></InputField>
                <InputField fieldRef={password2Field} name="password2" label="password again" type="password" error={formErrors?.password2}></InputField>
                <Button variant="primary" type="submit">Register</Button>
            </Form>
        </Body>
    );
}
3
  • in React you shouldn't be calling imperative functions like toast here in order to render something. Even the docs seem to say that this function isn't supposed to be used in React. You want to use the <Toaster> component and render it conditionally depending on the value of formErrors. Commented Jul 10, 2022 at 21:17
  • @RobinZigmond where does it say not to call toast ? It actually says Call it to create a toast from anywhere, even outside React. Make sure you add the <Toaster/> component to your app first. Which I've already added the component to the <Body> Commented Jul 10, 2022 at 21:38
  • I was precisely referring to that "outside React" clause. None of the examples are in a React context, it looks to me like this function is there to use if you want to use this library in vanilla JS. Commented Jul 11, 2022 at 20:00

2 Answers 2

3

I am not familiar with the <InputField> but I reckon error gets displayed right? It probably toasts and then rerenders the error and then toasts again after because you still have the error there.

What you probably want is useEffect(() => { //Code you want excecuted like toasting / alerting }, [formErrors]); Which fires the code in the useEffect scope everytime the variable in the array gets updated. Or you'd want to toast inside the scope that sets the form error which is simpler and arguably more elegant.

Also this is me being nosy but these inputfields could be self closing like this : <InputField props={props} /> UX wise its also probably better to show invalid input before the user clicks register or to also stop them from clicking it all together. Hope this helps, happy coding!

Sign up to request clarification or add additional context in comments.

2 Comments

Cheers for this, yes InputField is just a wrap around a normal html input with labels and validation elements also. Will try with use effect and see what happens :)
That was what I was missing, I get it now. When the second variable passed to useEffect is updated it fires again!!! Cheers a bunch, you've helped me get over a mental hurdle.
0

You can try adding an id unique such as:

toast.success("Product added to cart", { id: product.id });

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.