Home > Software engineering >  Using refs and .reduce scroll to the id of selected element with react with useState
Using refs and .reduce scroll to the id of selected element with react with useState

Time:05-05

sorry if the title doesn't make much sense.

I've been refactoring my code from this.state to useState, and I finally got things working except for the refs...

In my original code I was making individual axios calls and using this.state along with this refs code:

const refs = response.data.reduce((acc, value) => {
acc[value.id] = createRef();
return acc;
}, {});

but now I refactored my axios call to .all:

const getData = () => {
    const getSunSigns = axios.get(sunSignAPI);
    const getDecans = axios.get(decanAPI);
    const getNums = axios.get(numbersAPI);

    axios.all([getSunSigns, getDecans, getNums, refs]).then(
      axios.spread((...allData) => {
        const allSunSigns = allData[0].data;
        const getAllDecans = allData[1].data;
        const getAllNums = allData[2].data;

        setSunSigns(allSunSigns);
        setDecanSign(getAllDecans);
        setNumerology(getAllNums);
      })
    );
  };

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

so the response.data.reduce doesn't work cuz I'm not using 'response'.

I've tried several things but none worked.. unfortunately I deleted all the previous code but this is what I currently have, which works but obviously only takes one api:

const refs = sunSigns.reduce((acc, value) => {
    acc[value.id] = createRef();
    return acc;
  }, {});

  onClick = (id) => {
    refs[id].current.scrollIntoView({
      behavior: "smooth",
    });
  };

from the research I've done and the code I've tried I'm sure I'd have to map through the apis and then maybe use the reduce(???).. but I'm really not entirely sure how to go about it or how to rephrase my google search to get more accurate results.

what I'm trying to do specifically: on certain pages an extra nav bar appears with the symbol of a specific sign/number. the user can click on one and it'll scroll to that specific one. I'm going to have several pages with this kind of feature so I need to dynamically set refs for each api.

any help or guidance will be highly appreciated!!

edit**

the above codes are in my Main component and this is where I'm setting the refs:

  return (
    <div className='main'>
      <div className='main__side-container'>
        <SideBar />
        <div className='main__card-container'>
          <Card
            sunSigns={sunSigns}
            numerology={numerology}
            decanSign={decanSign}
            refs={refs}
          />
        </div>
      </div>
      <div className='main__bottom-container'>
        <BottomBar
          sunSigns={sunSigns}
          numerology={numerology}
          onClick={onClick}
        />
      </div>
    </div>
  );
}

then this is the card:

export default function Card({ sunSigns, decanSign, refs, numerology }) {
    return (
        <>
          <div className='card'>
            <Switch>
              <Route path='/astrology/western/zodiac'
                render={(routerProps) => <Zodiac refs={refs} sunSigns={sunSigns} />}
              />
              <Route path='/numerology/pythagorean/numbers'
                render={(routerProps) => <NumberPage refs={refs} numerology={numerology} />}
              />
            </Switch>
          </div>
        </>
      );
    }

and then this is the Zodiac page:

export default function Zodiac({ sunSigns, refs }) {
  return (
    <>
      <div className='zodiac__container'>
        <TitleBar text='ZODIAC :' />
        <div className='card-inset'>
        <div className='container-scroll'>
          <SunSignsList sunSigns={sunSigns} refs={refs} />
        </div>
        </div>
        
      </div>
    </>
  );
}

and the SunSignsList component:

export default function SunSignsList({ sunSigns, refs }) {
  return (
    <>
      <div className='sunsignsitemlist'>
        <ul>
          {sunSigns.map(sign => {
            return (
              <SunSigns
                refs={refs}
                key={sign.id}
                id={sign.id}
                sign={sign.sign}
                />
                );
              })}
        </ul>
      </div>
    </>
  );
} 

and the SunSigns component:

export default function SunSigns({
  id,
  sign,
  decans,
  refs
}) {
  return (
    <li ref={refs[id]}>
      <section className='sunsigns'>
        <div className='sunsigns__container'>
          <div className='sunsigns__header'>
            <h3 className='sunsigns__title'>
              {sign}
              {decans}
            </h3>
            <h4 className='sunsigns__symbol'>{symbol}</h4>
      </section>
    </li>
  );
}

the above code is where my ref code is currently accessing correctly. but the end goal is to use them throughout several pages and comps in the same manner.

CodePudding user response:

You can create three different objects holding the ref data for each list or if the id is same you can generate a single object which holds all the list refs.

const generateAllRefsObj = (...args) => {
  const genSingleListRefsObj = (acc, value) => {
    acc[value.id] = createRef();
    return acc;
  }
  return args.reduce((acc, arg) => ({ ...arg.reduce(genSingleListRefsObj, acc), ...acc }), {})
}

Usage

const allRefs = generateAllRefsObj(sunSigns,decanSign,numerology)
  • Related