skip to Main Content

I have a PagesContainer component that renders multiple Page components like this:

<PagesContainer>
  <Page />
  <Page />
  <Page />
  <Page />
</PagesContainer>

I want each Page component to know its respective page number (e.g., 1, 2, 3, 4).

I preferably do not want to pass it down as a prop (e.g., <Page number={1} />) because I will be programatically generating some <Page /> through loops, but not all of them. I also want to be able to reorder these pages freely and have each render the right number just by moving its position in the parent.

Ideally, I’d like a hook like so: const currentPage = useGetCurrentPage().

How do I get the <Page />‘s position in the parent?

2

Answers


  1. You can map() over the props.children, then use the index from map to pass along to the Child using cloneElement.

    I’ve incremented the index by 1 since your example does not start from 0.

    const App = () => (
        <PagesContainer>
            <Page />
            <Page />
            <Page />
            <Page />
        </PagesContainer>
    )
    
    const PagesContainer = (props) => props.children.map(
        (Child, index) => React.cloneElement(Child, { index: index + 1 })
    );
    
    const Page = ({ index }) => (<p>Page #{index}</p>);
    
    ReactDOM.render(<App />, document.getElementById("react"));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
    <div id="react"></div>
    Login or Signup to reply.
  2. You can use a forwardRef and useImperativeHandle and useState.

    
    export default function Test() {
      const parentRef = useRef(null);
      const childRef = useRef([]);
      useEffect(() => {
        if (childRef.current !== null) {
          childRef.current.forEach((cr, i) => {
            cr.setPageNumber(i + 1);
          });
        }
      });
      const addToRefs = (el) => {
        childRef.current.push(el);
      };
      return (
        <div ref={parentRef}>
          <Child ref={addToRefs} />
          <Child ref={addToRefs} />
          <Child ref={addToRefs} />
          <Child ref={addToRefs} />
        </div>
      );
    }
    
    const Child = forwardRef(function Child(props, ref) {
      const [pageNumber, setPageNumber] = useState(0);
      useImperativeHandle(ref, () => {
        return {
          setPageNumber(n) {
            setPageNumber(n);
          },
        };
      });
      return <div>{pageNumber}</div>;
    });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search