Refactor a React Class Component with useContext and useState Hooks

Kent C. Dodds
InstructorKent C. Dodds
Share this video with your friends

Social Share Links

Send Tweet
Published 6 years ago
Updated 4 years ago

We've got a pretty simple User class component that manages a bit of state and uses some context. Let's refactor this over to a function component that uses the useContext and useState hooks.

Instructor: [00:00] The component that's using this query component is actually a class component in itself. It's keeping track of a filter state and it needs to get the context and it needs to get the Github context just like our query component did.

[00:14] Let's go ahead and change this to use hooks. I'm just going to make a function called user and that's going to take our props. We're only taking username and that does not have a default, but we do have some prop types here so I'll take those out. We'll say user dot prop types equals that.

[00:29] Then let's take care of this Github context, so we're going to const client equals use context. Github context. Let's pull in use context from React. I also noticed we're going to want use state, so I'll just pull that in here while I'm up here.

[00:48] Next, let's go ahead and use that state. We're taking care of this context. We'll take care of this state now. We just have one item of state, so I'm going to say filter and set filter equals use state. We'll initialize it to the same value that we were initializing it to before. We've taken care of state initialization.

[01:07] Then we want to handle filter update. That actually is a function that accepts the filter and sets the filter value. That looks suspiciously like the set filter. We actually don't need to have a handle filter update. If we did then we could just move this right up and make this a const and then use it as we had before. Change this to set filter with that filter.

[01:29] That would work just as well, but that's basically just a pass through function, so it's worthless. Let's just get rid of it. Next, we have just our render. That's all that's left. What I'm going to do is I'm going to take this return all the way down to this closing parentheses. We'll put it right up here.

[01:47] Then I can get rid of the user class entirely. I'll hit save here. Now the last thing that we need to do to make sure that everything is working is look for this dot. If you find this dot anywhere in a component that should be a function component, then you've got a problem there. You missed something in your refactor.

[02:07] That's what we did. We missed dot context dot logout. What I'm actually going to do is I'll just get rid of this dot context. This logout is going to come from client here, so I'll just destructure that and logout. Let's look for this dot.

[02:19] There's another one. This is that function that we saw that was totally useless, so we're going to say set filter and this on update is just going to call our set filter with the new filter. Now let's look for this dot again. Looks like we are totally good to go. We can get rid of this component import now and let's make sure that everything is working.

[02:39] I'm just going to go. I can't see dots here. Go, loading our data. There it is. It's all working just like it was before, except it is using arguably a simpler function component. We have our context, we have our state, and we have our JSX that we're rendering.

shyam
shyam
~ 6 years ago

why did we not use something like onUpdate={() => setFilter(filter)}

shyam
shyam
~ 6 years ago

why did we not use something like onUpdate={() => setFilter(filter)}

Kent C. Dodds
Kent C. Doddsinstructor
~ 6 years ago

Where would filter come from in that case? Go ahead and try it and see what happens 😀

Alex Krasnicki
Alex Krasnicki
~ 6 years ago

While looking at the example code on github I've noticed that on this lesson's branch something was added to the Query component. Namely:

function useDeepCompareEffect(callback, inputs) {
  const cleanupRef = useRef()
  useEffect(() => {
    if (!isEqual(previousInputs, inputs)) {
      cleanupRef.current = callback()
    }
    return cleanupRef.current
  })
  const previousInputs = usePrevious(inputs)
}

I'm curious why you need to do it this way? Would't just running callback if the condition is met fit the bill? Why the cleanupRef?

Nash Kabbara
Nash Kabbara
~ 5 years ago

Thanks for the course Kent. I came to this lesson to make the same comment as Alex. Wouldn't something like below do the same?

function useDeepCompareEffect(callback, inputs) {
  let cleanup
  useEffect(() => {
    if (!isEqual(previousInputs, inputs)) {
      cleanup = callback()
    }
    return cleanup
  })
  const previousInputs = usePrevious(inputs)
}
Markdown supported.
Become a member to join the discussionEnroll Today