Avoid passing props deeply by using React Context

Dave Ceddia
InstructorDave Ceddia

Share this video with your friends

Send Tweet
Published 4 years ago
Updated 3 years ago

Passing data deep through a React app usually involves tedious passing of props or a complex solution like Redux. Here you’ll learn how to simplify an app that’s currently passing data with props by refactoring it to use React’s Context API.

Instructor: [00:00] We're looking at the route component of the app right now. It's rendering this login page because the current user is null right now. If I log in, the login page is going to call its onLogin prop. Then it'll put that into state and rerender the main page, which is what we're seeing here now.

[00:17] The current user is passed down to main page, which in turn passes it down to two more components without using it. It's passing it to header, which passes it down to user menu, which is this over here. Back to main page, we also see that current user is being passed to message list, which displays this personalized message over here.

[00:37] This works OK, but we have to pass current user through a bunch of components that don't care about it. Like main page doesn't actually use it. It just receives it and passes it along. Same with header.

[00:48] One way we can clean this up is using React Context. We can create a new context. I'm going to do that in a separate file called UserContext.js. In here, we'll import React and use React.createContext to create a new context.

[01:04] It takes a default value, but if we pass nothing, it'll just start off undefined. It returns an object, that we'll just call Context, with two properties. This has a Context.Consumer and a Context.Provider. We're just going to export default this whole Context object.

[01:23] If we go back over to index, we can import UserContext. We'll wrap the main page component with the UserContext.Provider. The provider takes a value prop, which is the value that it's going to pass down through this tree here. We're going to pass this state current user.

[01:42] Now let's drill down to where this context is used. We can use the new Context.Consumer to get that value out. Let's import our UserContext. Then we can wrap this message list with a UserContext.Consumer.

[01:58] If we try this right now, we're going to get an error because the consumer expects you to pass a single function as a child and we're passing an actual element here. What we want to do is wrap this element in a function. This function receives the value that we passed in from the provider.

[02:15] Here we passed a value prop. The value comes out as the first argument of this function. You can call it whatever you want. You can call it value. You can call it user and then use that user down here. Then we don't need a current user of prop anymore. You can see the message still renders out.

[02:32] Now we'll go over to the header, which is accepting the current user prop and passing it along, but it's not actually using that prop. If we drill into user menu, this is where we are actually using it, down here.

[02:44] Let's import that UserContext. Then we can wrap this component in UserContext.Consumer. Then we just need to wrap the content in a function which will receive the user. Then we can use user instead of props current user.

[03:04] Now we can go through and get rid of the current user prop everywhere it's no longer needed. Let's start up at index. We're passing current user to main page. We don't need to do that anymore. In main page, we were passing it along to header and message list. We can get rid of it here and from header and from message list.

[03:27] In message list, we already got rid of it. Header, we could remove the prop from here. We don't need to pass it to user menu. We already took care of user menu. If we save index, we can see the app is still working, but we don't have to pass the current user manually through all these levels anymore.

Steve
Steve
~ 3 years ago

I would've much preferred to have had the option to access to the code in it's beginning state rather than completed state. That way I could've coded along as it sticks in my head better that way :(

coisnepe
coisnepe
~ 3 years ago

Ditto to what @Steve said. IMO it makes little sense to be provided with the 'final' code instead of the code to be refactored.

Steve
Steve
~ 3 years ago

On the plus side. We can use this code to code along in the following lessons, although that doesn't help with this first lesson

Dave Ceddia
Dave Ceddiainstructor
~ 3 years ago

I'll see if there's a way I can provide the before + after versions of the code here, but in the meantime, here's the 'before' code.

coisnepe
coisnepe
~ 3 years ago

Awesome! Thanks for the quick reply, and for the link to the repo... and for the great tutorial.

Eric Laursen
Eric Laursen
~ 3 years ago

+1 Thanks for the 'before' code :)

Etenne-Joseph Charles
Etenne-Joseph Charles
~ 3 years ago

What do you use to instantly remove a prop on a react component ? You seem to be typing a keystroke to make it disappear

from:

"Counter value={someValue}"

to

"Counter"

Dave Ceddia
Dave Ceddiainstructor
~ 3 years ago

@Etenne-Joseph It's probably a vim command (I'm using the vscode-vim plugin throughout the lessons). At 3:22 for example, I'm putting the cursor at the beginning of currentUser, then typing "df " (d, f, spacebar) which deletes up-through-and-including the next space character.

anna
anna
~ 3 years ago

I would've much preferred to have had the option to access to the code in it's beginning state rather than completed state. That way I could've coded along as it sticks in my head better that way :( < Absolutely agree with you. The course doesn't have a sense for me

Md. Anam Hossain
Md. Anam Hossain
~ 2 years ago

Every time I made change and its logging out . How to fix this ?

Dave Ceddia
Dave Ceddiainstructor
~ 2 years ago

@Md. Anam Hossain - Being "logged in" only means that the user is set to something non-null, so you can temporarily set the user to a hardcoded value so that the app will render in a logged-in state. I did that a few times while building the examples.

Md. Anam Hossain
Md. Anam Hossain
~ 2 years ago

@Dave thank for the reply. Solved this problem by converting class to functional component.

function Root(){
  const [currentUser, setCurrentUser] = React.useState(JSON.parse(window.localStorage.getItem('currentUser')) || null);

  React.useEffect( () => {
    window.localStorage.setItem('currentUser', JSON.stringify(currentUser));
  }, [currentUser])

  const handleLogin = user => {
    setCurrentUser(user);
  };

  const handleLogout = () => {
    setCurrentUser(null);
  };
  return currentUser ? 
      <UserContext.Provider value={currentUser}>
        <MainPage
          currentUser={currentUser}
          onLogout={handleLogout}
        />
      </UserContext.Provider>
    : 
      <LoginPage onLogin={handleLogin} />
    ;
}