skip to Main Content

I have a children component (CollectionsBar.jsx) with a state named collection. When a div gets clicked, it updates the state.
I want to access the state in the parent’s component (Products.jsx). So when the state gets clicked, it changes the span tag.

Children Component:

import React, { useEffect, useState } from "react";
import useFetch from "../../hooks/useFetch";
import "./CollectionsBar.scss";

const CollectionsBar = () => {
  
  //SETTING DATA FOR useFetch
  const { data, loading, error } = useFetch(
    "/collection-list?populate[0]=collecao&populate=FundoTitulo&populate[1]=collecao.Imagem&populate=collecao.products&populate=collecao.products.img&populate=collecao.products.img2"
  );
  const minifyData = data?.attributes.collecao.data;
  const [collection, setCollection] = useState();

  //COLLECTIONS BAR COMPONENT
  return (
    <div className="sl-c-collections-bar">
      <div className="sl-l-bar__flex">
        {minifyData?.map((item, index) => {
          return (
            <div
              onClick={() => {
                setCollection(index);
              }}
              key={index}
              className="sl-c-flex__box"
            >
              <img
                src={
                  import.meta.env.VITE_REACT_APP_UPLOADS_URL +
                  item.attributes.Imagem.data.attributes.url
                }
                alt="Imagem Produto"
              />
            </div>
          );
        })}
      </div>
    </div>
  );
};

export default CollectionsBar;

Parent Component:

import React, { useEffect } from "react";
import useFetch from "../../hooks/useFetch";
import CollectionsBar from "../../components/CollectionsBar/CollectionsBar";
import "./Products.scss";
const Products = () => {
  return (
    <div className="sl-c-products">
      <div className="sl-l-products__flex">
        <span></span>
      </div>
      <CollectionsBar />
    </div>
  );
};

export default Products;

I just want to know the best way of doing it…

2

Answers


  1. Move const [collection, setCollection] = useState(); from the child to the parent component and then pass it down as a prop.

    // Parent component  
    ...  
    const [collection, setCollection] = useState();  
     return (
       <CollectionsBar setCollection={setCollection}/> 
     )
    ....  
    
    // Child component  
    const CollectionsBar = ({setCollection}) => {  
     // now you have your state in a parent component but can change it from the child one  
    }
    
    Login or Signup to reply.
  2. You have two options:

    1. Lifting up the state.

      This is the simpler way to do it. You just need to move your state to the parent component, and pass the setter down as a prop:

      const Products = () => {
      const [collection, setCollection] = useState();
        return (
          <div className="sl-c-products">
            <div className="sl-l-products__flex">
              <span></span>
            </div>
            <CollectionsBar setCollection={setCollection} />
          </div>
        );
      };
      

      export default Products;

    Then in the children you can use the setter:

    const CollectionsBar = ({setCollection}) => {
      const { data, loading, error } = useFetch("");
    
      return (
        <div className="sl-c-collections-bar">
          <div className="sl-l-bar__flex">
            {minifyData?.map((item, index) => {
              return (
                <div
                  onClick={() => {
                    setCollection(index);
                  }}
                  key={index}
                  className="sl-c-flex__box"
                >
                  <img
                    //...
                  />
                </div>
              );
            })}
          </div>
        </div>
      );
    };
    
    export default CollectionsBar;
    

    You also can pass the state if you need to use it in the children component.

    1. Use the useRef and forwardRef hooks

    If by any chance, you can’t lift up the state, you need to use ref. For this alternative, you should understand a bit more about how React works.

    Following the same example:

    Create a ref(childStateRef) and pass it as a prop to the children. Then, using the ref, you can call the fn you will create in the children comp.

    const Products = () => {
    const childStateRef = useRef<any>(null);
    console.log(childState.current.getCollection()) //this will log the state
      return (
        <div className="sl-c-products">
          <div className="sl-l-products__flex">
            <span></span>
          </div>
          <CollectionsBar ref={childStateRef} />
        </div>
      );
    };
    

    In your children component:

    Wrap the component with forwardRef and use "useImperativeHandle" to create a function to get your state.

    const CollectionsBar = forwardRef((props, _ref) => {
          const { data, loading, error } = useFetch("");
          const [collection, setCollection] = useState()
          useImperativeHandle(_ref, () => ({
            getCollection: () => {
            return collection;
            },
          }));
          return (
            <div className="sl-c-collections-bar">
              <div className="sl-l-bar__flex">
                {minifyData?.map((item, index) => {
                  return (
                    <div
                      onClick={() => {
                        setCollection(index);
                      }}
                      key={index}
                      className="sl-c-flex__box"
                    >
                      <img
                        //...
                      />
                    </div>
                  );
                })}
              </div>
            </div>
          );
        });
        
        export default CollectionsBar;
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search