Prince Codes
Prince Codes

Follow

Prince Codes

Follow
React Breakdown: useContext

React Breakdown: useContext

Prince Codes's photo
Prince Codes
·Feb 1, 2023·

4 min read

Play this article

Table of contents

  • Some Backstory
  • Let's Code 🚀
  • Conclusion

Are you tired of dealing with messy and hard-to-track states in React? Well, hold onto your seats because I've got the solution for you!

Let's goooo!!!! 🚀

Some Backstory

Let's say we have a React Application that has 3 components:

  • App.js - The main file

  • ComponentA.js - A component that displays my name.

  • ComponentB.js - A component that changes my name

First off, we'd need to store my name "Prince"

In React, every mutable data is stored as states which, can be used within component logic. For example:

// ./App.js
import React, { useState } from 'react';

export default function App() {
  const [name, setName] = useState('Prince');
  return (
    <div>
        <ComponentA />
        <ComponentB />
    </div>
  );
}

Problem #1

What if you need to use that state in multiple components?

No problem!

Solution

Just raise the state to a higher component and pass it down as props. This is called prop drilling.

// In the App.js

<ComponentA name={name}/>
<ComponentB name={name} setName={setName}/>

Then we can access the values as props (like function arguments).

// ./ComponentA.js
import React, {useContext} from 'react';
import NameContext from './NameContext';

export default function ComponentA({name}) {
  return (
    <div style={{ border: '1px solid red', margin: '1em' }}>
      <h1>Component A</h1>
      <p>Name: {name}</p>
    </div>
  );
}

The same thing for ComponentB:

// ./ComponentB.js
import React, {useContext} from 'react';
import NameContext from './NameContext';

export default function ComponentB({name, setName}) {
  return (
    <div style={{ border: '1px solid green', margin: '1em' }}>
      <h1>Component B</h1>
      <p>Change Name</p>
      <input type="text" defaultValue={name} onInput={e => setName(e.target.value)}/>
    </div>
  );
}

We set the default value of the input element to the state "name" and use an anonymous function to update the name to the input value.

Looks Good!

Problem #2

What happens when you need to pass the state to a large number of components or even a great-grandchild of another component?

That's where things can get messy, right?

Solution

Enter useContext, the game-changer!

With useContext, you can share a piece of state between multiple elements without the headache of prop drilling. No more passing the state down component by component. The state can be accessed directly, just as if it was defined there. Say goodbye to messy and hard-to-track state and hello to streamlined and efficient state management.

Let's Code 🚀

Let's set up a Context in the React application. First, we create a file name NameContext.js

// ./NameContext.js
import React, { createContext } from 'react';

const NameContext = createContext();

export default NameContext;

Then we use it in the App.js this way and, set the value to the state.

// ./App.js
import React, { useState } from 'react';
import ComponentA from './ComponentA';
import ComponentB from './ComponentB';
import NameContext from './NameContext';

export default function App() {
  const [name, setName] = useState('Prince');
  return (
    <div>
      <NameContext.Provider value={{ name, setName }}>
        <ComponentA />
        <ComponentB />
      </NameContext.Provider>
    </div>
  );
}

Awesome!! We that we have set up the Context.

To use it, we need the useContext hook.

// ./ComponentA.js
import React, {useContext} from 'react';
import NameContext from './NameContext';

export default function ComponentA() {
  const { name } = useContext(NameContext)
  return (
    <div style={{ border: '1px solid red', margin: '1em' }}>
      <h1>Component A</h1>
      <p>Name: {name}</p>
    </div>
  );
}

The same thing for ComponentB.

// ./ComponentB.js
import React, {useContext} from 'react';
import NameContext from './NameContext';

export default function ComponentB() {

  const {name, setName} = useContext(NameContext)
  return (
    <div style={{ border: '1px solid green', margin: '1em' }}>
      <h1>Component B</h1>
      <p>Change Name</p>
      <input type="text" defaultValue={name} onInput={e => setName(e.target.value)}/>
    </div>
  );
}

Output 🎉

Bonus Trick

Here's a trick to keep your context and state together.

We create a wrapper for the Context Provider and we can add all our states and data there.

// ./NameContext.js
import React, { useState, createContext } from 'react';

const NameContext = createContext();

const ProvideName = ({ children }) => {
  const [name, setName] = useState('Prince');
  return (
    <NameContext.Provider value={{ name, setName }}>
      {children}
    </NameContext.Provider>
  );
};

export { ProvideName };
export default NameContext;

Then we use it in the App.js this way instead.

// ./App.js
import React, { useState } from 'react';
import ComponentA from './ComponentA';
import ComponentB from './ComponentB';
import { ProvideName } from './NameContext';

export default function App() {
  return (
    <div>
      <ProvideName>
        <ComponentA />
        <ComponentB />
      </ProvideName>
    </div>
  );
}

Conclusion

That's all for today guys. We learnt about the useContext hook and the Context API. Hope you liked it. Until next time 🙌

 
Share this