Mr.Grease
Mr.Grease

Reputation: 197

How to pass props through a parent between two react components

I have two different react components placed one after the other in my app named SearchBar and InfiniteScroller;

function App() {

  const [searchTerm, setSearchTerm] = useState("");

  return (
    <div className="App">
      <SNavbar></SNavbar>
      <MainLogo></MainLogo>
      <SearchBar search={setSearchTerm}></SearchBar>
      <hr/>
      <InfiniteScroller term={searchTerm}/>
      <Footer/>
    </div>
  );
}

The search bar component has its own state where it updates a search term as its input is being edited and it calls the setSearch function of its parent when the button is clicked (the function is passed as a prop in the parent)

function SearchBar(props)
{
  const [search,setSearch] = useState("");

    return(      
        <Container className="Search-Bar">
        <Row>
          <Col>
          <InputGroup >
    <FormControl
      placeholder="What are we making today?" 
      onChange={event => setSearch(event.target.value)}
    />
    <Button onClick={() => props.search(search)}>
      Go!
    </Button>
  </InputGroup>
          </Col>
        </Row>
      </Container>)
}

The search term that is updated by the SearchBar component is passed onto the InfiniteScroller component as a property and is set as the searchTerm field in its state object.

class InfiniteScroller extends React.Component
{

    constructor(props)
    {
      super(props);
      this.state =   
        {
        items:[],
        page:1,
        hasMore:true,
        searchTerm:props.term
      };
    }

    render(){
    return(
<InfiniteScroll
  dataLength={this.state.items.length}
  next={this.fetchData}
  hasMore={this.state.hasMore}
  loader={<h4>Loading...</h4>}
  endMessage={
    <p style={{ textAlign: 'center' }}>
      <b>Yay! You have seen it all</b>
    </p>
  }
>
          <Row>
          {this.state.items.map((i, index) => (
            <Col key={index} lg="2" md="4" sm="6" xs="12">
              <ImageCell className="ImageCell" link = {this.state.items[index].link}> - #{index}</ImageCell>
            </Col>
          ))}
</Row>
  
</InfiniteScroll>
    )
}
}

However when the setSearchTerm function of App.js is triggered by pressing the button on the SearchBar component, the InfiniteScroller does not seem to get updated. As the SearchTerm field of its state still comes up as "undefined" and the component itself does not re-render to represent the change in property. I want the InfiniteScroller to completely re-render itself and make some API calls to populate itself with content, How can I achieve this?

So far I've tried adding in HTML tags that have the SearchTerm property in them to check if react skips re-rendering components that don't "use" any properties but that has not worked.

Upvotes: 0

Views: 121

Answers (1)

Nick Vu
Nick Vu

Reputation: 15520

The props' change does not make the UI re-rendering but the states' change does. It has 2 potential ways to fix have a proper UI re-rendering.

For the first one, you can add key attribute to your component that will help you do a trick for re-rendering whenever key gets changed

<InfiniteScroller term={searchTerm} key={searchTerm}/>

The second way, you can update your local states of that component by componentDidUpdate (useEffect in function-based components)

class InfiniteScroller extends React.Component
{

    constructor(props)
    {
      super(props);
      this.state =   
        {
        items:[],
        page:1,
        hasMore:true,
        searchTerm:props.term
      };
    }

    //update states according to props change
    componentDidUpdate(prevProps) {
       if(this.props.searchTerm !== prevProps.searchTerm) {
          setState({ searchTerm: this.props.searchTerm })
       }
    }

    render(){
    return(
<InfiniteScroll
  dataLength={this.state.items.length}
  next={this.fetchData}
  hasMore={this.state.hasMore}
  loader={<h4>Loading...</h4>}
  endMessage={
    <p style={{ textAlign: 'center' }}>
      <b>Yay! You have seen it all</b>
    </p>
  }
>
          <Row>
          {this.state.items.map((i, index) => (
            <Col key={index} lg="2" md="4" sm="6" xs="12">
              <ImageCell className="ImageCell" link = {this.state.items[index].link}> - #{index}</ImageCell>
            </Col>
          ))}
</Row>
  
</InfiniteScroll>
    )
}
}

Upvotes: 1

Related Questions