Home > database >  useState setter function is not updating state in react
useState setter function is not updating state in react

Time:01-27

I am using the useIsDirty hook in two components, CustomCodeEditor and EditorFooter, to track whether the code in the Editor has been modified. The hook returns an isDirty state and a setIsDirty function to update it. When I call setIsDirty(true) in the CustomCodeEditor component, the state is updated, but when I call setIsDirty(false) in the EditorFooter component, it doesn't seem to update the isDirty state. I believe this is because the EditorFooter component does not have access to the updated state. Anyone, please help me with this.

useIsDirty:

import { useEffect, useState } from "react"

const useIsDirty = () => {
  const [isDirty, setIsDirty] = useState(false)

  useEffect(() => {
    const handleBeforeUnload = (event) => {
      if (isDirty) {
        event.preventDefault()
        event.returnValue = ""
        alert("You have unsaved changes, are you sure you want to leave?")
      }
    }
    console.log("Diryt:", isDirty)
    window.addEventListener("beforeunload", handleBeforeUnload)
    return () => {
      window.removeEventListener("beforeunload", handleBeforeUnload)
    }
  }, [isDirty])

  return { isDirty, setIsDirty }
}

export default useIsDirty

CustomCodeEditor

import Editor from "@monaco-editor/react"

import useIsDirty from "../../hooks/useIsDirty"

const CustomCodeEditor = () => {
  const { isDirty, setIsDirty } = useIsDirty()

  console.log("isDirty:", isDirty)
  return (
        <div className="bg-[#1e1e1e] h-full">
          <Editor
            onChange={(value) => {
              updateCode(value || "")
              setIsDirty(true) // updating state
            }}
          />
        </div>
  )
}

export default CustomCodeEditor

EditorFooter

import Button from "../reusable/Button"

const EditorFooter = () => {
  const { setIsDirty } = useIsDirty()

  const handleSave = async () => {
    setIsDirty(false)
  }

  return (
    <div>
      <Button
        onClick={handleSave}
      >
        Save
      </Button>
      <Button
        onClick={handleSave}
      >
        Submit
      </Button>
    </div>
  )
}

export default EditorFooter

CodePudding user response:

Hooks are not singleton instances.. when you use useIsDirty somewhere.. it always create new instance, with unrelated states to other ones. If you want to share this state you need to use Context

const IsDirtyContext = createContext(undefined);

const IsDirtyProvider = ({ children }): ReactElement => {
     const [isDirty, setIsDirty] = useState(false)
    return <IsDirtyContext.Provider value={{isDirty, setIsDirty}}>{children}</IsDirtyContext.Provider>;
};

and then you should wrap your commponent tree where you wanna access it with IsDirtyProvider

after that, you can even create your custom hook that will just return that context:

const useIsDirty = () => {
  return useContext(IsDirtyContext)
}

CodePudding user response:

Looking at your question, it looks like you are trying to use the same state in both components. However, the state doesn't work like that. A new instance is created whenever you make a call to useIsDirty from a different component.

If you want to use the state value across two components. You can do that using one of the following ways.

1 - Use a parent and child hierarchy.

Steps

  • Create a parent component and wrap the two components inside the parent component.
  • Manage the state in the parent component and pass it using props to the child component.
  • Create a function in child components that will execute a function from the parent component. The parent component function will hold the code to update the state based on whatever value you receive from the child component.

Now you should be able to share your state between both components.


2 - Use the context api.

If you are not familiar with what context api is, below is a brief explanation.

Context api helps you share data between components, without the need of passing them as a prop to each and every component.

You can use createContext and useContext hooks from context api.

createContext is used to create a new context provider.

useContext hook is used to manage the state globally. You can get the value from context using this function. Whenever the state is updated the value will be reflected globally.

Note - Any component that needs to use the value inside the useContext should be wrapped inside the useContext provider component.

Steps to create a context provider. To create a context you just need to use the react hook createContext

  • Create a context using below code

    const isDirtyContext = createContext();

  • Wrap your components in the context provider

    import {IsDirtyContext} from './path/filename'

    <IsDirtyContext.Provider value={[isDirty, setIsDirty]}>{children}</IsDirtyContext.Provider>

  • If your context is in a separate file, then you can import it into any child component using the import statement.

    import {IsDirtyContext} from './path/filename'

  • Use the context

    const [isDirty] = useContext(IsDirtyContext);

Now the isDirty state value is available globally in all components.

Hope this information helps you. Please upvote if this helps you understand and solve the problem.

  • Related