I am workin on a project with React and I need to update react state with react hook when new data is inserted to my database. I am using contextAPI but as far as I am concerned, the way my components are structured I can't update that state with the data I got back from my database. Here is the more detail with my code:
I have a component in which data exists with useState
const [questions, setQuestions] = useState([]);
useEffect(() => {
(async () => {
const resp = await axios.get(
`${process.env.REACT_APP_BACKEND_URL}/product/${productId}/question`
);
setQuestions(resp.data);
})();
}, []);
And I have another component called PostQuestion which sends post request to my backend and insert new question to my database. They are completely unaware of each other's existence. we can pass down the props to the children and nested children by using contextAPI. but PostQuestion component is not the child of that component where all the questions data exist. That's how I understand contextAPI. But both of them are children of another component. But I don't want to place my
const [questions, setQuestions] = useState([]);
in that component which is the parent of those two components. What can I do to update that questions state inside my PostQuestion component?
Here is my PostQuestion component code
const postQuestionHandler = async (e) => {
e.preventDefault();
try {
const resp = await axios({
url: `${process.env.REACT_APP_BACKEND_URL}/product/${productId}/question`,
method: "POST",
data: {
question: questionInput.question.value,
userId,
},
});
if (resp.status === 200) {
setShowForm(false);
}
} catch (err) {
alert(err);
}
};
return <SecondaryBtn disabled={questionInput.question.error}>
Submit Your Question
</SecondaryBtn>
Summary
I have 2 components which don't have parent to child relationship. I want to update the state in component 1 by calling setState when I got back new data in component 2. I don't want to have state in those two components parent because it is already cluttered enough.
If I use Redux, there will be no such problem. Perhaps there is a way to do it in contextAPI too.
CodePudding user response:
Multiple solutions:
- You could poll in an interval.
- Store the state in a parent component (at some point they have so share one) and pass it down.
- Utilize a global state management tool like redux, or the context API of react. I would recommend the context API as it's built in.
CodePudding user response:
I think Context API is the best way.
You can create a context with React.createContext
, and then a component that will be encapsulating your context provider. So, a PostsContext
and then a PostsProvider
that would store the posts state and pass both the state and the setState to the PostsContext.Provider
.
Then, to make it simpler, create a usePosts
hook and on those children that need to consume the context, simply do const [posts, setPosts] = usePosts()
.
Alright, so you'd have:
- A Context;
- A Provider component of your own, that would use the Context;
- A hook that would consume the Context with
useContext
;
Just to make it clearer:
- A context:
const PostsContext = React.createContext();
- A provider:
function PostsProvider(props) {
const [posts, setPosts] = React.useState([]);
return <PostsContext.Provider value={{ posts, setPosts }} {...props} />
}
- A hook:
function usePosts() {
const context = React.useContext(PostsContext);
function postQuestionHandler(newPost) {
// ...
context.setPosts((posts) => [...posts, post]);
}
return [context.posts, postQuestionHandler]
}
And then you can use the PostsProvider
to encapsulate the children, and those components can access the context using the usePosts
hook.
That would be a complete cenario, you can divide the logic some other ways, like not creating a custom hook nor a custom Provider. I, personally, would prefer to just lift the state in this case, but as you said your parent component is already handling too much, perhaps that's a good option.