Yes, you can do that. In fact, it is good practice to do that. To avoid unnecessary re-renders and infinite loops, either include setData in the dependency array of the Child's useEffect or wrap the data in a useCallback in the Parent component. Also, it is recommended to initialise the data to some initial value when using useState. In your case, I would initialise it to null -> const [data, setData] = useState(null)
Example with the dependency array:
function Child({ setData }) {
useEffect(() => {
setData("new data"); //Calling setter here to update
}, [setData]);
return (...);
}
If you want to pass it to a child that is multiple levels deep, I would suggest using Context. With context, you can then use the useState in any child and you don't need to pass it as props throughout all of the children between the Parent and the Child that you want to use it in.
Example with the Context:
// Step 1. Create context
const MyContext = React.createContext();
// Step 2. Create a custom hook that will return Context
// This will allow you to use the context in any number of children more easily.
// And it will also make sure that it is only used within Parent component
const useData = () => {
const context = React.useContext(MyContext);
if (!context) {
throw new Error("useData must be used within a <Parent />");
}
return context;
};
function Parent() {
const [data, setData] = useState(null);
const value = [data, setData];
// Step 3. Use Context Provider and pass the value of the props that
// need to be used by any children down the tree
return (
<MyContext.Provider value={value}>
<Child />
</MyContext.Provider>
);
}
function Child() {
return <ChildTwo />;
}
function ChildTwo() {
// Step 4. Extract the prop from the Context that you need using custom hook
const [data, setData] = useData();
useEffect(() => {
setData("new data"); //Calling setter here to update
}, [setData]);
return (...);
}