This codemod refactors React components by moving useState hooks from parent components to child components when the state and setter function (setState) are passed down as props. The goal is to simplify the parent components and allow child components to manage their own internal state when appropriate.
You can find the implementation of this codemod in the Studio here
Example
Case 1: Basic Prop Drilling
Before:
tsfunction Parent() { const [count, setCount] = useState(0); return <Child count={count} setCount={setCount} />; } function Child({ count, setCount }) { return ( <div> <p>{count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); }
After:
tsfunction Parent() { return <Child />; } function Child() { const [count, setCount] = useState(0); return ( <div> <p>{count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); }
Case 2: Handling Destructured Props
Before:
tsfunction Parent() { const [rating, setRating] = useState(null); return ( <> <h1>Product</h1> <ProductRating rating={rating} setRating={setRating} /> </> ); } function ProductRating({ rating, setRating }) { return ( <> <label htmlFor="rating">Rating</label> <input type="number" id="rating" onChange={(e) => setRating(parseInt(e.target.value))} /> {rating && <p>Rating is {rating}</p>} </> ); }
After:
tsfunction Parent() { return ( <> <h1>Product</h1> <ProductRating /> </> ); } function ProductRating() { const [rating, setRating] = useState(null); return ( <> <label htmlFor="rating">Rating</label> <input type="number" id="rating" onChange={(e) => setRating(parseInt(e.target.value))} /> {rating && <p>Rating is {rating}</p>} </> ); }
Case 3: Conditional Rendering
The codemod does not move useState hooks in parent components if the child component is conditionally rendered. This is to avoid breaking logic that depends on certain conditions being met for state management.
Before:
tsfunction Parent() { const [count, setCount] = useState(0); return ( <div> {condition ? ( <Child count={count} setCount={setCount} /> ) : ( <OtherChild /> )} </div> ); } function Child({ count, setCount }) { return ( <div> <p>{count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); }
After:
tsfunction Parent() { const [count, setCount] = useState(0); return ( <div> {condition ? ( <Child count={count} setCount={setCount} /> ) : ( <OtherChild /> )} </div> ); } function Child({ count, setCount }) { return ( <div> <p>{count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); }