Have you ever heard of Prop Drilling? For beginners of React.js learners, it is a need-to-know term.
When I created an App with React.js, I wanted to pass props down to the grandchild component from the parent component, so I also needed to pass the props to the child components, as it is a rule of using props.
According to the public documents,
React components use props to communicate with each other. Every parent component can pass some information to its child components by giving them props.
In the following code, I did Prop Drilling. I passed the props down to TodoList component and then, I passed those to Todo component.
// APP.js
import { TodoList } from "./components/TodoList/TodoList";
function App() {
const watchingCheckBox = (id) => {
// Some Operation
};
const handleRemoveTodo = (id) => {
// Some Operation
};
const reEditTodoName = (id, name) => {
// Some Operation
};
let filteredTodos = todos;
// Some Operation
return (
<div className="App">
<TodoList
todos={filteredTodos}
watchingCheckBox={watchingCheckBox}
handleRemoveTodo={handleRemoveTodo}
reEditTodoName={reEditTodoName}
/>
</div>
);
}
export default App;
// TodoList.jsx
import React from "react";
import Todo from "../Todo/Todo";
function TodoList({
todos,
watchingCheckBox,
handleRemoveTodo,
reEditTodoName,
}) {
return todos.map((todo) => (
<Todo
todo={todo}
key={todo.id}
watchingCheckBox={watchingCheckBox}
handleRemoveTodo={handleRemoveTodo}
reEditTodoName={reEditTodoName}
/>
));
}
export default TodoList;
// Todo.jsx
import { HiOutlineXMark } from "react-icons/hi2";
import React from "react";
function Todo({ todo, watchingCheckBox, handleRemoveTodo, reEditTodoName }) {
const handleTodoClick = () => {
watchingCheckBox(todo.id);
};
const handleRemoveClick = () => {
handleRemoveTodo(todo.id);
};
const reEdit = () => {
if (// Some Condition) {
handleRemoveTodo(todo.id);
} else {
reEditTodoName(todo.id, reEditName);
}
};
return (
<div>
<input
type="checkbox"
checked={todo.isCompleted}
onChange={handleTodoClick}
/>{" "}
<label>
{todo.name}
</label>
<button onClick={handleRemoveClick}>
<HiOutlineXMark />
</button>
</div>
);
}
export default Todo;
It seems like a bucket brigade. If the props-passing hierarchy is shallow, that is fine, but as your application grows, the hierarchy gets deeper. Then, you must pass down props to multiple components, which is not a good way to write clean and reusable code so it is called Prop drilling.
What is Prop drilling?
Prop drilling is a pattern in React where data is passed down from one component to another component, even if the intermediate components do not need the data. This often occurs when data needs to be passed from a high-level parent component down to a low-level child component that is several levels deep in the component tree. In order to pass data from the parent component to the child component, props are used.
So what should we use in place of Prop Drilling?
It is the Context API!
What is Context API?
Context API is a feature in React that allows data to be passed down through the component tree without having to pass props manually at every level. It provides a way to share data among components without the need to explicitly pass the data as props to each component that needs it.
In the following code, I changed my code with Context API. I used createContext()
, SomeContext.Provider
and useContext()
. I also combined two files(TodoList.jsx and Todo.jsx) into one file.
createContext
createContext lets you create a context that components can provide or read.
const SomeContext = createContext(defaultValue)
SomeContext.Provider
Wrap your components into a context provider to specify the value of this context for all components inside:
function App() {
const [theme, setTheme] = useState('light');
// ...
return (
<ThemeContext.Provider value={theme}>
<Page />
</ThemeContext.Provider>
);
}
useContext
useContext is a React Hook that lets you read and subscribe to context from your component.
const value = useContext(SomeContext)
↓My code
// App.js
import { createContext } from "react";
import { TodoList } from "./components/TodoList/TodoList";
export const FilteredTodosContext = createContext();
export function App() {
const watchingCheckBox = (id) => {
// Some Operation
};
const handleRemoveTodo = (id) => {
// Some Operation
};
const reEditTodoName = (id, name) => {
// Some Operation
};
let filteredTodos = todos;
// Some Operation
return (
<div className="App">
<FilteredTodosContext.Provider
value={{
filteredTodos,
watchingCheckBox,
handleRemoveTodo,
reEditTodoName,
}}
>
<TodoList />
</FilteredTodosContext.Provider>
</div>
);
}
// TodoList.jsx
import { HiOutlineXMark } from "react-icons/hi2";
import React, { useContext, createContext } from "react";
import { FilteredTodosContext } from "../../App";
const TodosContext = createContext();
export function TodoList() {
const { filteredTodos } = useContext(FilteredTodosContext);
return filteredTodos.map((todo) => (
<TodosContext.Provider value={todo} key={todo.id}>
<Todo />
</TodosContext.Provider>
));
}
export function Todo() {
const todos = useContext(TodosContext);
const { watchingCheckBox, handleRemoveTodo, reEditTodoName }
= useContext(FilteredTodosContext);
const handleTodoClick = () => {
watchingCheckBox(todos.id);
};
const handleRemoveClick = () => {
handleRemoveTodo(todos.id);
};
const reEdit = () => {
if (// Some Condition) {
handleRemoveTodo(todo.id);
} else {
reEditTodoName(todo.id, reEditName);
}
};
return (
<div>
<input
type="checkbox"
checked={todo.isCompleted}
onChange={handleTodoClick}
/>{" "}
<label>
{todos.name}
</label>
<button onClick={handleRemoveClick}>
<HiOutlineXMark />
</button>
</div>
);
}
Context API simplifies data management by allowing components to access data without passing it through multiple layers of props. In addition, it allows for global state management, which is helpful when multiple components need to share state.
Therefore, many developers prefer to use Context API.
Top comments (0)