TransWikia.com

Re-render a sibling component using hooks

Stack Overflow Asked by Programmer1 on November 15, 2021

I am new to react and I’m trying to get the one component to re-render from another component.

Here’s my code:

const Parent = () => {
    return (
        <div>
            <Child1 />
            <Child2 />
        </div>
    )
}

What I intend to do is update Child1 when there is some trigger from Child2.

One way I can think of is to get the parent component to re-render so both Child1 and Child2 will be updated. I tried to do this by lifting the state but it doesn’t seem to re-render each of the child components. Here’s the code

const Parent = () => {
    const [value, setValue] = useState('')

    const handlePost = (newValue) => {
        setValue(newValue)
    }

    return (
        <div>
            <Child1 />
            <Child2 onPost={handlePost} />
        </div>
    )
}

const Child2 = (props) => {
    // This function is executed when there is a trigger. 
    // In this case, when a post request is made to the server
    const onPost() => {
        props.handlePost('new value')
    }
}

Edit:
The reason why the component(s) needs to be re-rendered is because they are making changes to the API and these changes need to be reflected on the screen. It has nothing to do with any state variables.

3 Answers

From the comments in the post, it sounds like you have Child1 presenting results of a GET request (being done in Child1). Child2 can add or modify that state on the server with some kind of request and you want to trigger a re-render in order to make Child1 refresh the state.

The general problem is, that children should only re-render if props or their used contexts change. I see two options how to approach this:

  1. Lift the handling of the requests up into the parent. Put the results of the request as props into the child component you want to refresh.

  2. Make the sibling aware of the request having to reload by setting it to "dirty" in some way. Either through context or routing state around through the parent.

Usually it's best to go with option 1 if the components are not too deeply nested. It could look like this:

const Parent = () => {
  const [posts, setPosts] = useState([]);
  const fetchNewestPosts = useCallback(async () => {
    const fetched = await fetchPosts();
    setPosts(fetched);
  }, [fetchPosts, setPosts]);
  const handleSubmit = useCallback(async (event) => {
    const newPost = getValuesFromSubmitEvent(event);
    await sendNewPost(newPost);
    // you could even set the posts here to what you think the 
    // send request will result in (see Jonas Wilms answer), like
    // setPosts(posts => [newPost, ...posts]);
    await fetchNewestPosts();
  }, [fetchNewestPosts, getValuesFromSubmitEvent, sendNewPost]);

  useEffect(() => {
    fetchNewestPosts();
  }, [fetchNewestPosts]);

  return (
    <div>
      <Child1 posts={posts} />
      <Child2 submitNewPost={submitNewPost} />
    </div>
  );
);

const Child1 = ({posts}) => {
  return (
    <ul>{posts.map(post => <li key={post.id}>post.title</li>)}</ul>
  );
);

const Child2 = ({submitNewPost}) => {
  return (
    <form onSubmit={submitNewPost}>...</form>
  );
);

As a nice side-effect, Child1 and Child2 now need a lot less logic and can be styled independently of the fetchPosts and sendNewPost functions.

Answered by Narigo on November 15, 2021

Your question is an XY problem. In the example given it does not make sense that Child1 rerenders cause there is no need for it. From the comments your real problem is that you update one API, which is supposed to change the response of another API. If you however already know how the response will change, and that it will change, this can be reflected in one state that changes for both API calls:

 function useEntries() {
   const [entries, setEntries] = useState([]);

   useEffect(() => {
      setEntries(getEntries());
   }, []);

   function addEntry(entry) {
     postEntry(entry);
    setEntries(prev => [...prev, entry]);
   }

   return { entries, addEntry };
 }

 function Parent() {
   const { entries, addEntry } = useEntries();

   return <>
     <Entries entries={entries} />
     <AddEntry addEntry={addEntry} />
   </>;
  }
     

  

Answered by Jonas Wilms on November 15, 2021

Ciao, lets say that Child1 must be re-rendered on handlePost. Your parent component will be:

const Parent= () => {
const [value, setValue] = useState('')
const [rerender, setrerender] = useState(false)

const handlePost = (newValue) => {
    setValue(newValue);
    let setrerender_temp = rerender;
    setrerender(!setrerender_temp);
}

return (
    <div>
        <Child1 rerender={rerender} />
        <Child2 onPost={handlePost} />
    </div>
)
}

Then, in your Child1 component:

import React, { useReducer, useEffect } from 'react';
...

export default function Child1(props) {
   const [,forceRender] = useReducer((s) => s+1, 0);
   useEffect(() => forceRender(), [props.rerender]);
   ...
}

Answered by Giovanni Esposito on November 15, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP