0

Hello fellow devs

I am building a basic client in React to test some functionality of my API, admittedly my Node.js knowledge far surpasses my understanding of react, states and hooks.

The problem: I have 3 input fields, one for "username", one for "password" and one for "confirmPassword". Each of these inputs values update based of a "subscriber" useState hook. that I have set using a handleChange function with an onChange listener placed in each input.

I then have two buttons which rely on a separate use state hook called logOrReg to change based on if the user wants to login or register. This hook will adjust the function the submit button hits as well as the text in the submit button and the text in the the button used to switch to login or register.

On the first render when I launch the app the logOrReg state appears to be set correctly being submit button function: function postSub login-or-register button text: "Already have an account?" and Submit button text: "register".

When I start typing text into the inputs they update accordingly and so does the subscribers state via the handleChange function placed into each input. I then go to click the submit button, it then hits the postSub function but the the subscribers state does not get passed to the function and so the API receives blank values in its console and the subscriber.username and subscriber.password targets in the postSub function come out blank in a console.log on the client side.

However if I click "already have an account?" the buttons switch (with the text in the inputs staying the same) and then click "Don't have an account?" the buttons change back to the register state as expected. I then click "register" and the values from the subscriber state set in the inputs suddenly get passed over to the postSub function and the API receives the password and username and registers the new user.

Then if I try to update the values in the input fields again the updated values don't get passed to the postSub function but instead the values from the initial registration get passed to the function and subsequently the api returning a message on my API console stating that the user has already been registered.

When looking at my components and states in the React dev tolls in the google developer console they all appear to be updating correctly, so its super confusing that when I click submit the state of the inputs doesn't get passed to the function. My assumption is I am not understanding how the useState hooks work but if they are displaying correctly in the Dev tools I have no idea how to understand why the values are not being passed to the function.

I really hope this makes sense, its such a weird problem to describe.

The code:

import React, {useState, useEffect} from 'react'
import Pusher from 'pusher-js';
import axios from "axios";
import Table from '@mui/material/Table';
import TableHead from '@mui/material/TableHead';
import TableCell from '@mui/material/TableCell';
import TableRow from '@mui/material/TableRow';

const PUSHER_APP_KEY = "b4173b2b1b29274b8621";
const PUSHER_APP_CLUSTER = "eu";

function Resthome(e) {

    const pusher = new Pusher(PUSHER_APP_KEY, {cluster: PUSHER_APP_CLUSTER})

    // var [activeOrders,     setActiveOrders] = useState([])
    // axios.get("hhttp://localhost:3000/restaurants/getactiveorders")
    // .then(response => {         setActiveOrders(response)     })
    // console.log(activeOrders) This will be called when your component is mounted
    useEffect(() => {

        

        const channel = pusher.subscribe("rests")
        let mounted = true;
        if (mounted) {
            console.log("mounted")

            channel.bind("inserted", (data) => {
                console.log("inserted")
                console.log("data", JSON.stringify(data))

                alert("you have been subscribed")

            })
        }
        return (() => {
            pusher.unsubscribe("rests")
            mounted = false
        })
    }, []);

    // [] would ensure that useEffect is executed only once

    function LoginOrRegister() {

         const [subscriber,
            setSubscriber] = useState({
                username: "",
                password: "",
                confirmPassword: ""
            })

               const [logOrReg,
            setLogOrReg] = useState({type: "reg", function: postSub, submitButton: "register", switchButton: "already have an account?"})
       

        function handleChange(event) {

            const newValue = event.target.value;
            const inputName = event.target.name;

            setSubscriber(prevValue => {
                if (inputName === "username") {
                    return {username: newValue, password: prevValue.password, confirmPassword: prevValue.confirmPassword}
                } else if (inputName === "password") {
                    return {username: prevValue.username, password: newValue, confirmPassword: prevValue.confirmPassword}
                } else if (inputName === "confirmPassword") {
                    return {username: prevValue.username, password: prevValue.password, confirmPassword: newValue}
                }
            })

            console.log("set sub username", subscriber.username, "set sub password", subscriber.password)

        }

        function Switch(e) {

            e.preventDefault()

            if (logOrReg.type === "reg") {
                setLogOrReg({type: "log", function: logUser, submitButton: "login", switchButton: "Dont have an account?"})
            } else if (logOrReg.type === "log") {
                setLogOrReg({type: "reg", function: postSub, submitButton: "register", switchButton: "Already have an account?"})
            }

        }

        async function postSub(e) {
            e.preventDefault()

            console.log("password", subscriber.password, "username", subscriber.username)

            try {
                if (subscriber.password === subscriber.confirmPassword) {
                    await axios
                        .post("http://localhost:3000/restaurants/register", {

                        username: subscriber.username,
                        password: subscriber.password

                    })
                        .then(response => {
                            if (response.status === 201) {
                                console.log("request sent successfully")
                            }
                        })

                } else {
                    alert("Passwords dont match")
                }
            } catch (err) {
                console.log(err)
            }
        }

        async function logUser(e) {
            e.preventDefault()

            try {
                if (subscriber.password === subscriber.confirmPassword) {
                    await axios
                        .post("http://localhost:3000/restaurants/login", {

                        username: subscriber.username,
                        password: subscriber.password

                    })
                        .then(response => {
                            if (response.status === 201) {
                                console.log("request sent successfully")
                                console.log(response)
                            }
                        })

                } else {
                    alert("Passwords dont match")
                }
            } catch (err) {
                console.log(err)
            }
        }

        // var [logOrReg,     setlogOrReg] = useState({username: "", password: "",
        // confirmPassword: ""})

     

        return (
            <div>
                <form onSubmit={logOrReg.function}>
                    <h1>Signup</h1>
                    <input
                        name="username"
                        onChange={handleChange}
                        placeholder="username"
                        value={subscriber.username}
                        label="email"></input>
                    <input
                        name="password"
                        onChange={handleChange}
                        placeholder="password"
                        value={subscriber.password}
                        label="password"></input>
                    <input
                        name="confirmPassword"
                        onChange={handleChange}
                        placeholder="confirmPassword"
                        value={subscriber.confirmPassword}
                        label="password"></input>
                    <button type="submit">
                        <h3>
                            {logOrReg.submitButton}
                        </h3>
                    </button>
                </form>
                <button name="login-or-register" onClick={Switch}>
                    <h3>{logOrReg.switchButton}</h3>
                </button>
            </div>

        )
    };

    return (
        <div>
            <LoginOrRegister/>
            <Table>
                <TableHead>
                    <TableRow>
                        <TableCell>ID</TableCell>
                        <TableCell align="right">Total</TableCell>
                        <TableCell align="right">Item names</TableCell>
                        <TableCell align="right">Status</TableCell>
                        <TableCell align="right">Time Placed</TableCell>
                    </TableRow>
                </TableHead>
            </Table>
        </div>
    )
}

export default Resthome

2 Answers 2

1

Reason

The problem here is that your function have not a reference to your state as described here at react documentation

Explanation

If you store a function in state the function captures the variable from outer scope (AFAIK copy operation).

When your state changes your function still holds the old copy. If you want that your function captures the new state you have to recall it (create a new reference to that function in your state).

You could use "useEffect" to re-render your invocation function when your state changes

Possible solution

Example on StackBlitz

But...

And the end you should prefer to extract your function from your state and make the decision in a static function with a conditional/switch-case on your state

0
0

For anyone who this may help see my solution below. I also cleaned up the birds nest and moved the LoginOrRegister component to its own file.

import React, {useState} from 'react'
import axios from "axios";

const MODE_REGISTER = "register";
const MODE_LOGIN = "login";

function LoginOrRegister({setCount, count}) {

    // REGISTER FUNCTION
    async function register() {

        console.log("password", formState.password, "username", formState.username)

        if (formState.password === formState.confirmPassword) {
            axios({
                method: "POST",
                data: {
                    username: formState.username,
                    password: formState.password
                },
                withCredentials: true,
                url: "http://localhost:3000/restaurants/register"
            }).then((res) => {console.log(res)
            
            setCount("allow")
            });

        } else {

            alert("passwords dont match")
        }

    };

    // LOGIN FUNCTION
    async function login() {

        console.log("password", formState.password, "username", formState.username)

        if (formState.password === formState.confirmPassword) {
            axios({
                method: "POST",
                data: {
                    username: formState.username,
                    password: formState.password
                },
                withCredentials: true,
                url: "http://localhost:3000/restaurants/login"
            }).then((res) => {
                
        console.log(res)
    setCount("allow")
    });

        } else {

            alert("passwords dont match")
        }

    };

    const [formState,
        setFormState] = useState({mode: MODE_REGISTER, username: "", password: "", confirmPassword: "", logState: "false"});

    function handleChange(event) {
        const newValue = event.target.value;
        const inputName = event.target.name;

        setFormState({
            ...formState,
            [inputName]: newValue
        })
    };

    function handleSubmit(event) {
        event.preventDefault();

        switch (formState.mode) {
            case MODE_LOGIN:
                login()
                // code block
                break;
            case MODE_REGISTER:
                register()
                // code block
                break;
            default:
                register() // defaulted to register
                // code block
        }
        console.log("from state", formState);
    };

    function switchMode() {
        setFormState({
            ...formState,
            mode: formState.mode === MODE_REGISTER
                ? MODE_LOGIN
                : MODE_REGISTER
        })

    };

    

    return (
        <div state={formState.logState}>
            <form onSubmit={handleSubmit}>
                <h1>Signup</h1>
                <input
                    name="username"
                    onChange={handleChange}
                    placeholder="username"
                    value={formState.username}
                    label="email"></input>
                <input
                    name="password"
                    onChange={handleChange}
                    placeholder="password"
                    value={formState.password}
                    label="password"></input>
                <input
                    name="confirmPassword"
                    onChange={handleChange}
                    placeholder="confirmPassword"
                    value={formState.confirmPassword}
                    label="password"></input>
                <button type="submit">
                    <h3>
                        {formState.mode}
                    </h3>
                </button>
            </form>
            <button name="login-or-register" onClick={switchMode}>
                <h3>Switch to {formState.mode === MODE_REGISTER
                        ? MODE_LOGIN
                        : MODE_REGISTER}</h3>
            </button>
        </div>

    )
};

export default LoginOrRegister
2
  • 1
    As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.
    – Community Bot
    Commented Jul 26, 2022 at 9:44
  • Thank you for your feedback, Ill write a more detailed explanation this evening.
    – CLAM
    Commented Jul 26, 2022 at 12:10

Not the answer you're looking for? Browse other questions tagged or ask your own question.