The useContext
hook allows you to leverage the React Context API within your function components. This lets your components provide data to their descendants in the component tree while avoiding "prop drilling" (i.e. passing props through components that are not concerned with the data).
When instantiating the hook, it takes one argument: a React context. This subscribes the component to the context and any updated values being provided by the Context.Provider
. Then, any data being provided by the parent component will be accessible in this component via the context.current
field.
// Create a context
const SearchContext = React.createContext();
// Subscribe to SearchContext
const context = useContext(SearchContext);
Ryan Harris: [0:00] Here on our SearchPage, we're rendering a SeachWidget, which has two children, SearchInput and SearchResults. These two components have related data, so we want to keep track of them here in our SearchPage, and then provide the data to the components using a context.
[0:16] First, let's add a state to keep track of the inputValue in our SearchInput component here. We'll come into SearchPage and say const [inputValue, setInputValue] = useState, and to start we'll set that to an empty string.
[0:34] Then, we want to add another state called SearchResults to keep track of the items that will be displaying here. That would be const [searchResults, setSearchResults] = useState, and we'll set this to an empty array to start.
[0:52] First, let's add a useEffect hook to fetch data using our fetchApiData function here. We'll come down here and add a useEffect. The first argument is going to be an anonymous function. The second argument is our dependency array, which will have one value in it, inputValue, meaning this hook will run only when inputValue changes.
[1:14] We'll come into our function here and say const filteredResults = fetchApiData and we'll pass in the inputValue. Then we'll set our search results to filteredResults.
[1:32] Now that we're fetching data from our API, we'll want to instantiate our contexts, so that we can pass data down to our descendant components. Let's come up to the top of the file and say, export const SearchContext = React.createContext.
[1:52] To give other components access to the values we'll be passing, we'll need to provide them using something called a Context.Provider. Down here, we'll say SearchContext.Provider and that will wrap our SearchWidget.
[2:08] Right now, the Provider is not passing any data, because we haven't defined our value prop. This is an object and we have two fields, results, which we'll set to searchResults, and setInputValue, which we'll set to setInputValue. This is the setter function from our useState above.
[2:29] Let's go into our SearchInput component. This is where our useContext hook is going to come into play. You may have noticed that in SearchPage we exported the SearchContext value, and that's because we need to import it here. We'll say import {SearchContext} from "./SearchPage".
[2:51] To subscribe this component to the provider, we will come into the component body and say const context=useContext, and we'll pass it the SearchContext value.
[3:02] To wire up our input so that when it changes, it changes the value we are storing in state in our SearchPage component, we will come down to our markup, come to the input and add an onChange prop and when the value changes, we'll want to call the handleInputChange function, which we have defined here.
[3:22] We will uncomment this because now, when the value changes, we can set it to the state in SearchPage using the setInputValue function that we're getting off of our context. In our SearchResults component, we're going to need to do the same thing.
[3:37] Let's import { SearchContext } from "./ SearchPage". Down in the body of our SearchResults component, we're mocking our context value, so our app renders, but let's replace this with the actual context value by saying const context=useContext and pass in the SearchContext. When we save, you will see that our data is now rendering.
[4:07] In summary, by using useContext and some local state variables here, we are able to centralize the logic and data fetching of our app. We can then provide these values to the components that need them on a case by case basis and avoid prop drilling.