Home > Net >  Updating Parent Component using useContext
Updating Parent Component using useContext

Time:06-01

I'm trying to change the state of a parent component from a child component. My Component structure is basically like this.

<Admin>
    <Nav/> //Nav Bar
    {changePass && props.children} //Change Password Form
</Admin>

My Nav, in turn has several child components:

  <NavBar>

    <NavItem menu="☰">
      <DropdownMenu/>
    </NavItem>
    
  </NavBar>

In the DropdownMenu component there are child button Components, Logout, Delete Account, and Change Password. The latter is from which I want to toggle the Change Password Component in Admin.tsx. In DropdownItem.tsx I have a bit of code to detect the button click:

//ChangePassContext.tsx
import { createContext } from "react";

export let ChangePassContext = createContext<boolean>(false)

 //DropdownItem.tsx
import { ChangePassContext } from "../Context/ChangePasswordContext/ChangePassContext";

 let changePass = useContext(ChangePassContext)

 //When window loads, get previous state from local storage
 if (destination === CHANGEPSSWD){
      if (localStorage.getItem('changePass') === "true"){
        changePass = !changePass
      } 
    }

    const handleMenu = (event: MouseEvent) => {
      event.preventDefault();
      if (destination === CHANGEPSSWD) {
   
        // Open Change Password Form
        if (changePass === false){
          localStorage.setItem('changePass', "true")
        } else {
          localStorage.removeItem('changePass')
        }
          changePass = !changePass
          console.log(changePass)
          window.location.reload() //I'll reference this in a moment
       } 
    }

I then call changePass in my Admin.tsx file and update it using what I have stored in my local storage:

let changePass = useContext(ChangePassContext)
if (localStorage.getItem("changePass") === "true"){
  changePass = !changePass
}

And as shown above, the changePass context controls whether the ChangePassword Form is opened or closed:

{changePass && props.children} //Change Password Form

The only problem is I can't get the state to update from my DropdownItem component to my Admin parent component. The only way I can get it to update is if I reload the page, as I do in the DropdownItem component, resulting in a very poor user Experience:

enter image description here

Ironically, as you can see, I have a working menu component (passing down props to children seems easier), and I did it with the useContext provider and useState. The problem is that when I try to set the context with useState, the context won't update, and I don't know why. Originally I tried setting my DropdownItem function to something simple like this:

  if (destination === CHANGEPSSWD) {
    // Open Change Password Form
    setChangePass(!changePass)
  } 

I also don't want to use localStorage at all but I can't seem to get useContext to work without it.

I'm very frustrated, especially with how ugly this code is. I hope someone can help.

CodePudding user response:

Make your Admin file like this:

//Admin.tsx
import { createContext } from "react";
export const ChangePassContext = createContext();

function App() {
 const[changePass, setChangePass] = useState({});

 return (
  <ChangePassContext.Provider value={[changePass, setChangePass]}>
  <Admin>
    <Nav/> //Nav Bar
    {changePass && props.children} //Change Password Form
  </Admin>
  </UserContext.Provider>
  );
 }

export default App;

Then you can use it in your DropdownItem.tsx

//DropdownItem.tsx
import { useContext } from 'react';
import { ChangePassContext } from '../../App';
const DropdownItem = () => {
const[changePass, setChangePass]= useContext(ChangePassContext);
if (destination === CHANGEPSSWD) {
  // Open Change Password Form
  setChangePass(!changePass)
}
return (
   //your code
  );
};
  • Related