Thumbnail for null by null

7m 21s1,287 words~7 min read
Auto-Generated

[0:00]A common pattern when we're writing code that renders UI is that we'll have a main application component that manages some shared state. And then it renders a bunch of child components that just display parts of that state. And then it also renders a few form components that let the user edit that state. Now, in React, we have this one-way data flow where state is owned by some component, and then that component passes that state down to its children. And then the child components can't directly update that state. Instead, what they do is they call an event handler function that's also passed down from the parent. And then it's the parent component that updates the state and passes down the new state to the child. This pattern is sometimes called lifting state up, and it's super common and super useful when you have a component that needs to update the state of its parent. But it's not the only pattern for updating state, and sometimes it can become a little bit cumbersome. Specifically, in this video, I want to talk about the case when you have an input component and that input component has some internal state that it wants to manage itself. And then it also wants to be able to notify its parent of changes to that internal state. Let's take a look at an example. So here I have a component called my input that renders a div that contains an input and a button. Now, this input has a value that's controlled by the state variable text, and the default value of that state variable is just an empty string. And then whenever the value of this input changes, we update the state variable text. So this is a typical controlled input pattern. Then we also have a button that when you click on it, alerts the current value of the input. So if I type something into the input, say hello, and then I click on the button, it alerts hello. So this is just an ordinary input component. But now let's say that the parent component of my input, so the app component wants to know whenever the text in my input changes. So how can we do that? Well, a common pattern would be to lift the state up. So we'd say, okay, the app component wants to know about the text. So let's create a text state variable in the app component. Let's pass that text state variable into my input, and then let's also pass a set text function down to my input. And then my input would be responsible for updating the text through the set text function. But the problem is that my input wants to have its own internal state because it has this button that when you click on it, alerts the current value of the input. So it wants to manage its own state. And then it also wants to be able to notify its parent of changes to that state. So how can we do that? Well, one way that we can do that is by using a ref. So instead of passing the text state variable and the set text function down to my input, let's create a ref in the app component. And let's call this ref my input ref. So we'll say const my input ref is equal to use ref. And then we'll pass this ref down to my input. So we'll say ref is equal to my input ref. Now, in order for my input to be able to receive this ref, we need to wrap it in forward ref. So let's import forward ref from React. And then let's wrap our my input component in forward ref. And then we'll receive the ref as a second argument to our component. Now, what we want to do is we want to expose the current value of the input to the parent. So in the app component, we want to be able to say my input ref dot current dot value. And then we want to get the current value of the input. Now, we can't directly do that because the input is not directly exposed to the parent. So what we need to do is we need to use the use imperative handle hook. So let's import use imperative handle from React. And then inside of my input, we'll call use imperative handle. And use imperative handle takes two arguments. The first argument is the ref that we received from the parent. And the second argument is a function that returns an object that contains the values that we want to expose to the parent. So here, we want to expose the value of the input. So we'll say value is equal to text. Now, in the app component, we can use the current value of the input. So let's create a button that when you click on it, alerts the current value of the input. So we'll say button on click alerts my input ref dot current dot value. So now if I type something into the input, say hello, and then I click on the button, it alerts hello. And then if I click on the button in the app component, it also alerts hello. So this is great. We're able to expose the internal state of my input to the parent. But now what if we also want to be able to set the value of the input from the parent? So let's say that in the app component, we have a button that when you click on it, sets the value of the input to world. How can we do that? Well, we can add a set value function to the object that we're returning from use imperative handle. So we'll say set value is equal to a function that takes a new value as an argument, and then it sets the text state variable to that new value. Now, in the app component, we can call my input ref dot current dot set value. So let's create a button that when you click on it, sets the value of the input to world. So we'll say button on click my input ref dot current dot set value world. So now if I type something into the input, say hello, and then I click on the button, it alerts hello. And then if I click on the set to world button, it sets the value of the input to world. And then if I click on the button in the app component, it alerts world. So this is great. We're able to expose the internal state of my input to the parent, and we're also able to set the internal state of my input from the parent. So this pattern is super useful when you have a component that wants to manage its own internal state. And then it also wants to be able to notify its parent of changes to that internal state. And then it also wants to be able to allow its parent to set that internal state. So this is a really powerful pattern that you can use to create more flexible and reusable components. Now, one thing to keep in mind is that you should use this pattern sparingly. It's generally better to lift state up to the parent component whenever possible. But in cases where you have a component that really needs to manage its own internal state, this pattern can be really useful. I hope this video was helpful. If you have any questions, please let me know in the comments below.

Need another transcript?

Paste any YouTube URL to get a clean transcript in seconds.

Get a Transcript