0

I am having trouble with React and MUI elements, I do not think it is MUI causing this.

With the code below I can only type 1 letter in the text box before my cursor leaves the text box, I then have to click on the text box again to type a 2nd letter, and so on...

I can eliminate this issue by removing the onChange and value fields on OutlinedInput, it then lets me type freely in the text box, but this creates a 2nd issue, which is when I click the show/hide password button it clears the text in the text box

I am having a global issue with my app, text boxes are cleared anytime a hook is updated

Thank you!

const [values, setValues] = React.useState({
        amount: '',
        password: '',
        weight: '',
        weightRange: '',
        showPassword: false,
      });
    
const handleChange = (prop) => (event) => {
        setValues({ ...values, [prop]: event.target.value });
};
    
    
const handleClickShowPassword = () => {
        setValues({
          showPassword: !values.showPassword,
        });
};
    
const handleMouseDownPassword = (event) => {
        event.preventDefault();
};

<FormControl sx={{ m: 1, width: '25ch' }} variant="outlined">
                    <InputLabel htmlFor="outlined-adornment-password">Password</InputLabel>
                    <OutlinedInput
                        id="outlined-adornment-password"
                        type={values.showPassword ? 'text' : 'password'}
                        value={values.password}
                        onChange={handleChange('password')}
                        endAdornment={
                        <InputAdornment position="end">
                            <IconButton
                            aria-label="toggle password visibility"
                            onClick={handleClickShowPassword}
                            onMouseDown={handleMouseDownPassword}
                            edge="end"
                            >
                            {values.showPassword ? <VisibilityOff /> : <Visibility />}
                            </IconButton>
                        </InputAdornment>
                        }
                        label="Password"
                    />
</FormControl>

Full file code:

import * as React from 'react';
import SERVERIP from '../..//constants';
import { styled, alpha } from '@mui/material/styles';
import ModalUnstyled from '@mui/base/ModalUnstyled';
import { Box, LinearProgress } from '@mui/material';

var userRole = "";

export default function UserArea(props) {
    const [userList, setUserList] = React.useState([]);
    const [addUserRole, setAddUserRole] = React.useState("user");
    const [deleteUser, setDeleteUser] = React.useState();
    const [deleteOpen, setDeleteOpen] = React.useState(false);
    const handleDeleteOpen = () => setDeleteOpen(true);
    const handleDeleteClose = () => setDeleteOpen(false);

    const StyledModal = styled(ModalUnstyled)`
        position: fixed;
        z-index: 1300;
        right: 0;
        bottom: 0;
        top: 0;
        left: 0;
        display: flex;
        align-items: center;
        justify-content: center;
    `;

    const Backdrop = styled('div')`
        z-index: -1;
        position: fixed;
        right: 0;
        bottom: 0;
        top: 0;
        left: 0;
        background-color: rgba(0, 0, 0, 0.5);
        -webkit-tap-highlight-color: transparent;
    `;

    const style = {
        width: 400,
        bgcolor: 'background.paper',
        border: '2px solid #000',
        p: 2,
        px: 4,
        pb: 3,
    };

    // Do this on screen load
    React.useEffect(() => {
        GetUsers();
      }, [])

    function HandleDeleteUser(user) {
        setDeleteUser(user);
        handleDeleteOpen();
    }

    function HandleUserLog(user) {
        localStorage.setItem('viewing-user', user);
        props.userDetails(true);
    }

    // Function for backend login api call
    function GetUsers() {
        const url = SERVERIP + 'get_users';
        const jwtToken = "Bearer " + localStorage.getItem('session-id');
        // Fetch request to backend
        fetch(url, {
          method: 'get',
          headers: {
            "Content-Type": "application/json",
            'authorization': jwtToken,
          },
        }).then(response => response.json())
        .then(json => {
          // On success response
          if (json.Success) {
              setUserList(json.Data);
          } else {
            props.errorMsg({Status: false, Msg: json.Errors})
          }
        });
    }

    // Function for backend login api call
    function AddUser(event) {
        event.preventDefault();
        var username = document.getElementById("add-username").value;
        var fullName = document.getElementById("add-fullName").value;
        var password = document.getElementById("add-password").value;
        var verifyPassword = document.getElementById("add-verifyPassword").value;
        var yourPassword = document.getElementById("add-yourPassword").value;
        var roles = userRole;

        if (password === "" || verifyPassword === "" || yourPassword === "" || username === "" || fullName === "" || roles === "") {
            props.errorMsg({Status: true, Msg: "Missing required fields!"})
            return
        }

        if (password != verifyPassword) {
            props.errorMsg({Status: true, Msg: "Passwords do not match!"})
            return
        }

        const url = SERVERIP + 'new_user';
        const jwtToken = "Bearer " + localStorage.getItem('session-id');
        var postBody = {
            username: username,
            full_name: fullName,
            password: password,
            verifyPassword: verifyPassword,
            yourPassword: yourPassword,
            roles: roles
        }
        // Fetch request to backend
        fetch(url, {
          method: 'post',
          body: JSON.stringify(postBody),
          headers: {
            "Content-Type": "application/json",
            'authorization': jwtToken,
          },
        }).then(response => response.json())
        .then(json => {
          // On success response
          if (json.Success) {
            props.successMsg({Status: true, Msg: "User added!"});
            GetUsers()
            console.log(testVariable);
          } else {
            props.errorMsg({Status: true, Msg: json.Errors})
          }
        });
    }

    function DeleteUser(event) {
        event.preventDefault();
        var password = document.getElementById("delete-password").value;

        if (password === "" || deleteUser === null || deleteUser === undefined) {
            props.errorMsg({Status: true, Msg: "Missing required fields!"})
            return
        }

        const url = SERVERIP + 'delete_user';
        const jwtToken = "Bearer " + localStorage.getItem('session-id');
        var postBody = {
            username: deleteUser,
            password: password,
        }
        // Fetch request to backend
        fetch(url, {
          method: 'post',
          body: JSON.stringify(postBody),
          headers: {
            "Content-Type": "application/json",
            'authorization': jwtToken,
          },
        }).then(response => response.json())
        .then(json => {
          // On success response
          if (json.Success) {
            props.successMsg({Status: true, Msg: "User deleted!"});
            GetUsers()
          } else {
            props.errorMsg({Status: true, Msg: json.Errors})
          }
        });
    }

       // Fuction for filtering the graph database table (search)
       function FilterTable() {
        // Declare variables
        var input, filter, table, tr, td, i, txtValue;
        input = document.getElementById("userFilter");
        filter = input.value.toUpperCase();
        table = document.getElementById("UserTable");
        tr = table.getElementsByTagName("tr");
    
        // Loop through all table rows, and hide those who don't match the search query
        for (i = 0; i < tr.length; i++) {
        td = tr[i].getElementsByTagName("td")[1];
        if (td) {
            txtValue = td.textContent || td.innerText;
            if (txtValue.toUpperCase().indexOf(filter) > -1) {
            tr[i].style.display = "";
            } else {
            tr[i].style.display = "none";
            }
        }
        }
    }

    function UpdateArea() {
        return (
            <div className="Content-Row">
                <div className="Content-Box-Device-Right">
                    <h3>User List</h3>
                    <form className="formStyle7">
                        <ul>
                            <li>
                                <label htmlFor="userFilter">Search</label>
                                <input type="text" id="userFilter" onKeyUp={FilterTable}/>
                            </li>
                            <li>
                            </li>
                        </ul>
                    </form>
                    <table id="UserTable">
                        <thead>
                            <tr>
                                <th>Full Name</th>
                                <th>Username</th>
                                <th>Role</th>
                                <th> </th>
                                <th> </th>
                            </tr>
                        </thead>
                        <tbody>
                            {userList.map((user, index) => { return ( <tr key={index}><td>{user.fullName}</td><td>{user.username}</td><td>{user.role}</td><td><button className="blue-button" onClick={() => HandleUserLog(user.username)}>LOGS</button></td><td><button className="blue-button" onClick={() => HandleDeleteUser(user.username)}>DELETE</button></td></tr> )})}
                        </tbody>
                    </table>
                </div>
                <div className="Content-Box-Add-User">
                    <h3>Add New User</h3>
                    <UserRoleDropdown/>
                    <form className="formStyle7">
                        <ul>
                            <li>
                                <label htmlFor="username">Username</label>
                                <input id="add-username" type="text" name="username" maxLength="100"/>
                            </li>
                            <li>
                                <label htmlFor="fullName">Full Name</label>
                                <input id="add-fullName" type="text" name="fullName" maxLength="100"/>
                            </li>
                            <li>
                                <label htmlFor="password">Password</label>
                                <input id="add-password" type="password" name="password" maxLength="100" />
                            </li>
                            <li>
                                <label htmlFor="verifyPassword">Verify Password</label>
                                <input id="add-verifyPassword" type="password" name="verifyPassword" maxLength="100" />
                            </li>
                            <li>
                                <label htmlFor="yourPassword">Your Password</label>
                                <input id="add-yourPassword" type="password" name="yourPassword" maxLength="100" />
                            </li>
                            <li>
                                <button className="blue-button" onClick={AddUser}>ADD USER</button>
                            </li>
                        </ul>
                    </form>
                    <Test />
                </div>
            </div>
        )
    }

    return (
        <div>
            <StyledModal
                  aria-labelledby="unstyled-modal-title"
                  aria-describedby="unstyled-modal-description"
                  open={deleteOpen}
                  onClose={handleDeleteClose}
                  BackdropComponent={Backdrop}
                >
                  <Box sx={style}>
                    <h3>Delete User: {deleteUser}</h3>
                    <form className="formStyle7">
                        <ul>
                            <li>
                                <label htmlFor="password">Account Password</label>
                                <input id="delete-password" type="password" name="password" maxLength="100"/>
                            </li>
                            <li>
                                <button className="blue-button" onClick={DeleteUser}>DELETE USER</button>
                            </li>
                        </ul>
                    </form>
                  </Box>
            </StyledModal>
            <UpdateArea />
        </div>
    )
}

function UserRoleDropdown() {
    function HandleUserRole(role) {
        userRole = role;
        console.log(userRole);
    }

    return (
            <div className="dropdown">
                <button className="blue-button">Select a role</button>
                <div className="dropdown-content">
                    <a href="#" onClick={() => HandleUserRole("user")}>Standard User</a>
                    <a href="#" onClick={() => HandleUserRole("admin")}>Administrator</a>
                </div>
            </div>
    )
}
kbessemer
  • 125
  • 1
  • 10
  • This might help you understanding what is going on https://stackoverflow.com/questions/69837357/problem-keyboard-hide-after-write-one-char-in-textinput-textinput-inside-flatli/69840602#69840602 – Giovanni Londero Mar 11 '22 at 11:14
  • Ok, I am understanding the component is being rendered again, but I am not understanding how to solve this issue. This is one of my first React apps, I do not really understand the solution in the post you linked. I understand you made ListHeaderComponent no longer a function, but how would I apply that to my issue? – kbessemer Mar 11 '22 at 11:26
  • Ok I think I understood your link, I moved the password input field outside of the main function, into its own function. Then I used a global variable in the jsx file and changed this variable with the input field's onChange method, this seems to be working – kbessemer Mar 11 '22 at 11:36
  • Yes, the issue is your components are rendered again, that should set you in the right direction debugging your issue. I just added a comment since I'm not able to provide a full answer yet. Could you provide some more code to better understand how components are implemented? – Giovanni Londero Mar 11 '22 at 11:38
  • Hmm well, a solution with a global variable doesn't seem great, maybe we should look for other solutions. – Giovanni Londero Mar 11 '22 at 11:39
  • I added the full file's code. You can see at the bottom of the file I moved UserRoleDropdown outside of the main function. When this component was inside the main function selecting its value would reset the add user form fields. This is resolved. Can you think of a better solution? – kbessemer Mar 11 '22 at 11:56
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/242847/discussion-between-kbessemer-and-giovanni-londero). – kbessemer Mar 11 '22 at 12:04

1 Answers1

1

Instead of passing the name password as a prop use the following code:

    <input
      className='form-control'
      type='text'
      id='userName'
      name='userName'
      value={userName}
      onChange={onChange}
      placeholder='Username'
      required
    />

And in your onChange function do the following

const onChange = (e) => {
  setFormData((prevState) => ({
    ...prevState,
    [e.target.name]: e.target.value,
  }))
}

And make sure to use the same name in input and state.

Dharmik Patel
  • 1,041
  • 6
  • 12