Hai Tien
Hai Tien

Reputation: 3117

Component Child is not re-rendered although state updated

I did not use reducer for this case to update state change on component. I send prop to child Component and I want it will be updated every time state changed on parent Component.

In the mother Component, I set state currentPage by default is 1;

const [pager, setPager] = useState({
    currentPage: 1
  });

const propToSend = pager.currentPage;

//and make trigger change:
const onClick = (e) => {
 setPager({...pager, currentPage:5});
}
....return...
<Pagination pageProp={propToSend} />

in Pagination Component I got state and set it into new State:

const Pagination = (props) => {
  const currentPage = props.pageProp
  const [pager, setPager] = useState({
    currentPage
  });

 const changeActivePage(num)=> {
   setPager({..pager, currentPage:num});
 }
  .....
 const paginomal = ListPagers.map(num => (
    <li key="num" onClick={changeActivePage(num)}>{num}</li>
 ));
  return(
    <Fragment>
      {paginomal}
    </Fragment>
  )
}

If you can see if I make change pager on mother Compoment with function

//and make trigger change:
const onClick = (e) => {
 setPager({...pager, currentPage:5});
}

Then nothing happen on pager.

Can you tell me how to trigger to change from mother Component because state is changed with click event on Child?

Upvotes: 0

Views: 48

Answers (2)

Drew Reese
Drew Reese

Reputation: 202721

You need to update your pagination state when the currentPage from props updates. Using an effect hook can accomplish this. The useEffect hook triggers once for the first mount of the component, then again each time a value in its dependency array (the second argument) is changed. In this case, when the currentPage value is changed from, say, 1 to 2.

const Pagination = (props) => {
  const currentPage = props.pageProp;

  const [pager, setPager] = useState({
    currentPage
  });

  useEffect(() => setPager(currentPage), [currentPage]);

  const changeActivePage(num)=> {
    setPager({..pager, currentPage:num});
  }
  .....
  const paginomal = ListPagers.map(num => (
    <li key="num" onClick={changeActivePage(num)}>{num}</li>
  ));
  return(
    <Fragment>
      {paginomal}
    </Fragment>
  )
}

But now you've two sources of truth!! And it can be difficult to keep them in sync, hence the above issue you had. It may be better to instead pass the setPager function from the "mother" to the child as a callback. It then updates the state stored in the parent component, the single source of truth.

const Pagination = ({ pageProp, onPageChange }) => {
  const currentPage = pageProp;

  const changeActivePage(num)=> {
    onPageChange({ currentPage: num });
  }
  .....
  const paginomal = ListPagers.map(num => (
    <li key="num" onClick={changeActivePage(num)}>{num}</li>
  ));
  return(
    <Fragment>
      {paginomal}
    </Fragment>
  )
}

Then from Mother

const [pager, setPager] = useState({
    currentPage: 1
  });

const propToSend = pager.currentPage;

//and make trigger change:
const onClick = (e) => {
  setPager({...pager, currentPage:5});
}
....return...
<Pagination pageProp={propToSend} onPageChange={setPager} />

Upvotes: 1

shivamragnar
shivamragnar

Reputation: 414

If you do not want to keep a separate state in your child component then you should pass you onClick method as props to your child component and then use it something like below

const paginomal = ListPagers.map(num => (
    <li key="num" onClick={() => onClick(num)}>{num}</li>
  ));

And in your parent component you can write something like

//and make trigger change:
const onClick = (num) => {
 setPager({...pager, currentPage:num});
}
....return...
<Pagination pageProp={propToSend} onClick={onClick}/>

Upvotes: 1

Related Questions