5

I have a react component which manage user logging in and out, when user type email and password in the login field the whole component (Navbar) re-render to Dom in every keystroke unnecessarily thus reduces speed.

How can I prevent Navbar from re-rendering when user type their credential in login fild ?

import React, { useContext,useState } from 'react';
import { Postcontext } from '../contexts/Postcontext';
import axios from 'axios';

const Navbar = () => {


  const { token,setToken } = useContext(Postcontext);
  const [email,setEmail] = useState('');  **state manages user email for login**
  const [password,setPassword] = useState(''); **state manages user password for login**
  const[log,setLog] = useState(true)  **state manages if user logged in or not based on axios post request**


  const login=(e)=>{
    //function for login using axios
      })
  }
  const logout=(e)=>{

    //function for logout using axios
  }
  return (

   <div className="navbar">


     {log?(
        <form>
        <input value={email} type="text" placeholder="email" onChange={(e)=>setEmail(e.target.value)}/>
        <input value={password}  type="text" placeholder="password" onChange={(e)=>setPassword(e.target.value)}/>
        <button onClick={login}>login</button>
        </form>
     ):(
       <button onClick={logout}>logout</button>
     )}
    </div>
  );
}

export default Navbar;

4 Answers 4

5

It is because it is same component which needs re-render to reflect input text changes. If you want your email to change but not effect Navbar then create a child component and move inputs into that component, manage input values using useState() there in child component and when you finally submit and user is logged in then you can either update some global state like redux store or global auth context to reflect and rerender Navbar.

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

2 Comments

I have made another component with just login form, and now Navbar does not re-render when child component changes, But please note that the child component rerender when I type in the input area, I hope there is no way to stop the rerendering until I complete typing in the input field
You can do so by using uncontrolled component and the setting/saving value on onBlur but that is not a good approach. Don't worry about these renders, this is not a big deal unless you have millions of inputs in your page
4

So, I had the same issue and I was able to solve it using useRef and useCallback and I will try to explain in Q&A form. Sorry if I am not that clear, this is my first StackOverFlow comment and I am a beginner in React :)

Why useRef? React re-renders every time it sees a component has updated by checking if previous and current object are same or not. In case of useRef it checks the object Id only and not the content inside it i.e. value of current inside the Ref component. So if you change the value of current React will not consider that. (and that's what we want)

Why useCallback? Simply because it will run only when we call it or one (or more) of the dependencies have changed. As we are using Ref so it won't be called when the current value inside it has changed.

More info: https://reactjs.org/docs/hooks-reference.html

Based on above info your code should look like this (only doing login part):

import React, { useContext, useRef } from 'react';


const App = () => {


  const emailRef = useRef(null);  
  const passwordRef = useRef(null);
  const logRef = useRef(null);


  const loginUpdate = useCallback( async (event) => {
        event.preventDefault();
        // Your logic/code
        // For value do: 
        // const email = emailRef.current.value;
    
     }, [emailRef, passwordRef, logRef]);

  
  return (

   <div className="navbar">


     {log?(
        <form>
        <input
          ref={emailRef}
          type="text"
          placeholder="email"
         />
        <input
          ref={passwordRef}
          type="text"
          placeholder="password"
         />
        <button onClick={loginUpdate}>login</button>
        </form>
     ):(
       // Not doing this part because I am lazy :)
       <button onClick={logout}>logout</button>
     )}
    </div>
  );
}

export default App;

Comments

0

Had a few typos. It works for me

https://codesandbox.io/s/cold-sun-s1225?file=/src/App.js:163-208

import React, { useContext,useState } from 'react';
// import { Postcontext } from '../contexts/Postcontext';
// import axios from 'axios';

const App = () => {


  // const { token,setToken } = useContext();
  const [email,setEmail] = useState('');  
  const [password,setPassword] = useState(''); 
  const[log,setLog] = useState(true)  


  const login=(e)=>{
    //function for login using axios
      }

  const logout=(e)=>{

    //function for logout using axios
  }
  return (

   <div className="navbar">


     {log?(
        <form>
        <input value={email} type="text" placeholder="email" onChange={(e)=>setEmail(e.target.value)}/>
        <input value={password}  type="text" placeholder="password" onChange={(e)=>setPassword(e.target.value)}/>
        <button onClick={login}>login</button>
        </form>
     ):(
       <button onClick={logout}>logout</button>
     )}
    </div>
  );
}

export default App;

2 Comments

it works fine but I was concerned about performance. appreciate your effort
This still re-renders on every input stroke
0

I faced the issue of losing focus on my input fields whenever changing the prefilled values as react was re-rendering the whole component. The pre-filled values were coming from context variable and was getting assigned to multi-level children components. It's an app to use dynamic number input fields group.

After spending a lot of hours and observing the changes in my DOM I figured out the issue. It was issue with keys

Changed the codes from

if (prefilledData.length) {
        prefilledData.forEach((ftd, fin) => {
            prefilledData[fin].id = uuid();
        });
    }

to

if (prefilledData.length) {
        prefilledData.forEach((ftd, fin) => {
            prefilledData[fin].id = `filled-data-${fin}`;
        });
    }

Because the component was called from inside a map function

{prefilledData.map((comp, ind) => {
      return (<FormComponent key={`${comp.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.