9

Im trying to pass and update a state with useContext;

App.js

import Home from './components/Home'
const UserContext = createContext();

function App() {
  const [name, setName] = useState('Name');

  return (
      <UserContext.Provider value={{name, setName}}>
        <Home/>
      </UserContext.Provider>
  );
}

export default App;

Home.js

import UserContext from '../../App'

function Home() {
    const user = useContext(UserContext);

    return (
        <>
        <label>Your name:</label>
        <input type='text' onChange={e => user.setName(e.target.value)} />
        <p>{user.name}</p>
        </>
    )
}

export default Home;

Im getting this error

TypeError: Cannot read properties of undefined (reading 'name');

How is the correct way to pass state between components with useContext?

2
  • The code you have posted does not match the error you are receiving. Firstly, it would not compile since you do not export the UserContext. Which means you cannot import it into your Home Component. Once you have done that, your app does work. I've copy pasted your code into a Create-react-app and it works. Commented Oct 2, 2021 at 20:33
  • How do I export context? Commented Oct 2, 2021 at 20:54

2 Answers 2

21

You need to export your UserContext, so it can be imported in the components that need it:

export const UserContext = React.createContext();

function App() {
  const [name, setName] = useState('Name');

  return (
    <UserContext.Provider value={{ name, setName }}>
      <Home />
    </UserContext.Provider>
  );
}

Afterwards you can import it in your App component:

import { UserContext } '../../App'

function Home() {
    const user = useContext(UserContext);

    return (
        <>
            <label>Your name:</label>
            <input type='text' onChange={e => user.setName(e.target.value)} />
            <p>{user.name}</p>
        </>
    )
}
Sign up to request clarification or add additional context in comments.

6 Comments

I didnt know that, thx alot
If you need a variable in a file and it is not defined in that file using const, let or var - you need import it. IF you need to import it, it has to be exported in some other file.
I thought the <.Provider> was exporting it to all the children somehow
Simple and elegant
export const UserContext = React.createContext(); produces the error An argument for 'defaultValue' was not provided. Apparently, a defaultValue must be provided, e.g. createContext({}) ?
|
9

1. Setting parent state for dynamic context

Firstly, in order to have a dynamic context which can be passed to the consumers, I'll use the parent's state. This ensures that I've a single source of truth going forth. For example, my parent App will look like this:

const App = () => {
  const [name, setName] = useState("John");
  const value = { name, setName };

  return (
   ...
  );
};

The name is stored in the state. We will pass both name and the setter function setName via context later.

2. Creating a context

Next, I created a name context like this:

// set the defaults
const NameContext = React.createContext({
  name: "John",
  setName: () => {}
});

Here I'm setting the defaults for name ('John') and a setName function which will be sent by the context provider to the consumer(s). These are only defaults and I'll provide their values when using the provider component in the parent App.

3. Creating a context consumer

In order to have the name switcher set the name and also showing, it should have the access to the name setter function via context. It can look something like this:

const NameSwitcher = () => {
const { name, setName } = useContext(NameContext);
 return (
    <label>Your name:</label><br />
    <input type='text' onChange={e => setName(e.target.value)} />
    <p>{name}</p>
  );
};

Here I'm just setting the name to input value but you may have your own logic to set name for this.

4. Wrapping the consumer in a provider

Now I'll render my name switcher component in a NameContext.Provider and pass in the values which have to be sent via context to any level deeper. Here's how my parent App look like:

const App = () => {
   const [name, setName] = useState("John");
   const value = { name, setName };

   return (
    <Name.Provider value={value}>
      <NameSwitcher />
    </Name.Provider>
   );
};

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.