7

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.

Nyi Nyi Hmue Aung
  • 587
  • 1
  • 7
  • 19

2 Answers2

2

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:

  1. A Context;
  2. A Provider component of your own, that would use the Context;
  3. 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.

Leonardo
  • 111
  • 1
  • 5
1

Multiple solutions:

  1. You could poll in an interval.
  2. Store the state in a parent component (at some point they have so share one) and pass it down.
  3. 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.
Elias
  • 3,592
  • 2
  • 19
  • 42
  • I am using contextAPI. But like I mentioned, as far as I know how contextAPI works, it cant be done with contextAPI since they are not parent and child. – Nyi Nyi Hmue Aung Sep 24 '21 at 14:50
  • @NyiNyiHmueAung There is always a shared parent. At some point they will be rendered inside `App` – Elias Sep 24 '21 at 14:51
  • @NyiNyiHmueAung are the components siblings? I can make an example for that if you want to :) – Elias Sep 24 '21 at 14:52
  • Yes, there is absolutely a shared parent. But I don't want to have my state inside that shared parent because it doesn't make sense. I was thinking there will be other ways of doing it. Other alternatives. – Nyi Nyi Hmue Aung Sep 24 '21 at 14:52
  • Well using a context you could at least put the `Context` inside the parent. For example if you have multiple pages that create, manage, and delete users, you could put the provider inside the router that serves those routes. – Elias Sep 24 '21 at 14:53
  • @NyiNyiHmueAung And you can always put the context provider inside `App` around `Content` (or whatever) if that's what you desire. I have an application where I did exactly that with a provider for theming values, and one for locale translations. – Elias Sep 24 '21 at 14:55
  • @NyiNyiHmueAung What exactly is your desired result? I see that you would like to use the context API. And the provider needs to be added to the DOM, but... you don't want to do that? Or do I get you wrong? – Elias Sep 24 '21 at 15:01