Table of contents
No, it's not
It's possible to pass setState
as a prop in React. However, it's not the recommended way to deal with updating state triggered by child components.
Passing down
setState
makes it hard to reason about the state of the component.State management should be encapsulated within the component that owns the state.
To show an example of bad code:
function ParentComponent() {
const [count, setCount] = useState(0);
return <ChildComponent setCount={setCount} />;
}
function ChildComponent({ setCount }) {
return (
<button onClick={() => setCount((prevCount) => prevCount + 1)}>
Increment
</button>
);
}
The callback approach
A more common and recommended approach is to pass a callback to the child component and let it call the callback when the action happens.
It's a much cleaner approach:
Abstraction: You decide what should happen. The component only knows about the callback, not how it's implemented.
Flexibility: If you need more logic than just setState, you can include it in the callback that you pass down to the child component.
Reusability: The component can be reused in different contexts. It's not tied to the parent component.
Let's take a look at the good example:
function ParentComponent() {
const [count, setCount] = useState(0);
const incrementCount = () => {
setCount((prevCount) => prevCount + 1);
};
return <ChildComponent onIncrement={incrementCount} />;
}
function ChildComponent({ onIncrement }) {
return <button onClick={onIncrement}>Increment</button>;
}
Here we don't pass down setCount
as prop. Instead, the child component accepts onIncrement
as prop and calls it when the button is clicked. We have control over what happens, and we can add more logic to the callback if we want.
What about state in Context?
When it comes to dealing with React's Context API, it's recommended that the Context.Provider
should contain the useState
hooks. This ensures it's a single source of truth for the state.
This is much better than passing down state and setState as props to the Provider from a parent component.
Example of how good code looks like:
import React, { useState, useContext, createContext } from "react";
const CountContext = createContext();
function CountProvider({ children }) {
const [count, setCount] = useState(0);
const increment = () => {
setCount((prevCount) => prevCount + 1);
};
return (
<CountContext.Provider value={{ count, increment }}>
{children}
</CountContext.Provider>
);
}
// Consumer component
function Counter() {
const { count, increment } = useContext(CountContext);
return (
<div>
<p>{count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
// App component
function App() {
return (
<CountProvider>
<Counter />
</CountProvider>
);
}