'How to make a useRef component?

Hi I have the following horizontal stack with images. I used two buttons on either end to scroll through the stack.

const PostDetails = props => {
  let scrl = useRef(null);
  const [scrollX, setscrollX] = useState(0);
  const [scrolEnd, setscrolEnd] = useState(false);

  //Slide click
  const slide = (shift) => {
    scrl.current.scrollLeft += shift;
    setscrollX(scrollX + shift);

    if (
      Math.floor(scrl.current.scrollWidth - scrl.current.scrollLeft) <=
      scrl.current.offsetWidth
    ) {
      setscrolEnd(true);
    } else {
      setscrolEnd(false);
    }
  };
  const scrollCheck = () => {
    setscrollX(scrl.current.scrollLeft);
    if (
      Math.floor(scrl.current.scrollWidth - scrl.current.scrollLeft) <=
      scrl.current.offsetWidth
    ) {
      setscrolEnd(true);
    } else {
      setscrolEnd(false);
    }
  };

 return(
        <Box>
            <Box style={{paddingLeft:'10%', paddingTop:'80px'} }>
                  <Flex justifyContent='center' alignItems='center'>
                    <Heading as='h4' size='md'>Top sales of all time</Heading>  
                  </Flex>         
                  <Box display='flex'>
                      {
                        scrollX !== 0 && (   
                          <Button className="prev" onClick={() => slide(-300)} style={{left: '0%', top: '50%', transform: 'translate(-30%, 500%)', }}>
                              <ArrowBackIcon/>
                              <i className="fa fa-angle-left"></i>
                          </Button>
                      )}
                      <Stack className='stackBar' ref={scrl} onScroll={scrollCheck} overflowX='scroll' scroll-behavior='smooth' direction={['column', 'row']} spacing='24px'>   
                      {topSalesList.obj_fulfilled_ask.map(i => {
                          const image = "https://ipfs.io/" + i.token.display_uri.replace(':', '');
                          return (
                            <Row tokenData = {i}/>
                          )
                        })
                      }   
                      </Stack>
                      {!scrolEnd && (
                      <Button className="next" onClick={() => slide(+300)} style={{left: '0%', top: '50%', transform: 'translate(10%, 500%)',}}>
                        <ArrowForwardIcon/>
                      </Button> 
                      )}
                  </Box>
              </Box>
 </Box>

Now this works perfectly fine, However if I want to add another replica stack and add the same two buttons on either side, I have to copy the same refs and supporting functions over and over shown below


  let scrlY = useRef(null);
  let scrl = useRef(null);
  const [scrollX, setscrollX] = useState(0);
  const [scrolEnd, setscrolEnd] = useState(false);

  //Slide click
  const slide = (shift) => {
    scrl.current.scrollLeft += shift;
    setscrollX(scrollX + shift);

    if (
      Math.floor(scrl.current.scrollWidth - scrl.current.scrollLeft) <=
      scrl.current.offsetWidth
    ) {
      setscrolEnd(true);
    } else {
      setscrolEnd(false);
    }
  };
  const scrollCheck = () => {
    setscrollX(scrl.current.scrollLeft);
    if (
      Math.floor(scrl.current.scrollWidth - scrl.current.scrollLeft) <=
      scrl.current.offsetWidth
    ) {
      setscrolEnd(true);
    } else {
      setscrolEnd(false);
    }
  };

  const [scrollY, setscrollY] = useState(0);
  const [scrolEndY, setscrolEndY] = useState(false);

  //Slide click
  const slideY = (shift) => {
    scrlY.current.scrollLeft += shift;
    setscrollY(scrollY + shift);

    if (
      Math.floor(scrlY.current.scrollWidth - scrlY.current.scrollLeft) <=
      scrlY.current.offsetWidth
    ) {
      setscrolEndY(true);
    } else {
      setscrolEndY(false);
    }
  };
  const scrollCheckY = () => {
    setscrollY(scrlY.current.scrollLeft);
    if (
      Math.floor(scrlY.current.scrollWidth - scrlY.current.scrollLeft) <=
      scrlY.current.offsetWidth
    ) {
      setscrolEndY(true);
    } else {
      setscrolEndY(false);
    }
  };

How can I make a ref component and just call it everytime?

Edit for clarity this is what I mean when I say adding another stack.

<Box>
                <Box style={{paddingLeft:'10%', paddingTop:'80px'} }>
                      <Flex justifyContent='center' alignItems='center'>
                        <Heading as='h4' size='md'>Top sales of all time</Heading>  
                      </Flex>         
                      <Box display='flex'>
                          {
                            scrollX !== 0 && (   
                              <Button className="prev" onClick={() => slide(-300)} style={{left: '0%', top: '50%', transform: 'translate(-30%, 500%)', }}>
                                  <ArrowBackIcon/>
                                  <i className="fa fa-angle-left"></i>
                              </Button>
                          )}
                          <Stack className='stackBar' ref={scrl} onScroll={scrollCheck} overflowX='scroll' scroll-behavior='smooth' direction={['column', 'row']} spacing='24px'>   
                          {topSalesList.obj_fulfilled_ask.map(i => {
                              const image = "https://ipfs.io/" + i.token.display_uri.replace(':', '');
                              return (
                                <Row tokenData = {i}/>
                              )
                            })
                          }   
                          </Stack>
                          {!scrollEnd && (
                          <Button className="next" onClick={() => slide(+300)} style={{left: '0%', top: '50%', transform: 'translate(10%, 500%)',}}>
                            <ArrowForwardIcon/>
                          </Button> 
                          )}
                      </Box>
                  </Box>
              </Box>
              <Box style={{paddingLeft:'10%', paddingTop:'20px'} }>
                  <Flex justifyContent='center' alignItems='center'>
                    <Heading as='h4' size='md'>Recent Sales</Heading>  
                  </Flex>         
                  <Box display='flex'>
                  {
                            scrollX !== 0 && (   
                              <Button className="prev" onClick={() => slide(-300)} style={{left: '0%', top: '50%', transform: 'translate(-30%, 500%)', }}>
                                  <ArrowBackIcon/>
                                  <i className="fa fa-angle-left"></i>
                              </Button>
                          )}
                      <Stack className='stackBar' ref={scrl} onScroll={scrollCheck} overflowX='scroll' scroll-behavior='smooth' direction={['column', 'row']} spacing='24px'>   
                      {latestSalesList.obj_fulfilled_ask.map(i => {
                          const image = "https://ipfs.io/" + i.token.display_uri.replace(':', '');
                          return (
                            <Row tokenData = {i}/>
                          )
                        })
                      }   
                      </Stack>
                      {!scrollEnd && (
                          <Button className="next" onClick={() => slide(+300)} style={{left: '0%', top: '50%', transform: 'translate(10%, 500%)',}}>
                            <ArrowForwardIcon/>
                          </Button> 
                          )}

                  </Box>
              </Box>


Solution 1:[1]

    const useScroll = (scrl) => {
      const [scrollX, setscrollX] = useState(0);
      const [scrolEnd, setscrolEnd] = useState(false);
      
      //Slide click
      const slide = (shift) => {
        scrl.current.scrollLeft += shift;
        setscrollX(scrollX + shift);
    
        if (
          Math.floor(scrl.current.scrollWidth - scrl.current.scrollLeft) <=
          scrl.current.offsetWidth
        ) {
          setscrolEnd(true);
        } else {
          setscrolEnd(false);
        }
      };
      const scrollCheck = () => {
        setscrollX(scrl.current.scrollLeft);
        if (
          Math.floor(scrl.current.scrollWidth - scrl.current.scrollLeft) <=
          scrl.current.offsetWidth
        ) {
          setscrolEnd(true);
        } else {
          setscrolEnd(false);
        }
      };
    return {scrollX,scrollEnd, slide, scrollCheck}
    }
export default useScroll;

Now to use this hook you can do this.

const PostDetails = props => {
      let scrl = useRef(null);
      const {scrollX, scrollEnd, slide, scrollCheck} = useScroll(scrl)
        
     return(
            <Box>
                <Box style={{paddingLeft:'10%', paddingTop:'80px'} }>
                      <Flex justifyContent='center' alignItems='center'>
                        <Heading as='h4' size='md'>Top sales of all time</Heading>  
                      </Flex>         
                      <Box display='flex'>
                          {
                            scrollX !== 0 && (   
                              <Button className="prev" onClick={() => slide(-300)} style={{left: '0%', top: '50%', transform: 'translate(-30%, 500%)', }}>
                                  <ArrowBackIcon/>
                                  <i className="fa fa-angle-left"></i>
                              </Button>
                          )}
                          <Stack className='stackBar' ref={scrl} onScroll={scrollCheck} overflowX='scroll' scroll-behavior='smooth' direction={['column', 'row']} spacing='24px'>   
                          {topSalesList.obj_fulfilled_ask.map(i => {
                              const image = "https://ipfs.io/" + i.token.display_uri.replace(':', '');
                              return (
                                <Row tokenData = {i}/>
                              )
                            })
                          }   
                          </Stack>
                          {!scrolEnd && (
                          <Button className="next" onClick={() => slide(+300)} style={{left: '0%', top: '50%', transform: 'translate(10%, 500%)',}}>
                            <ArrowForwardIcon/>
                          </Button> 
                          )}
                      </Box>
                  </Box>
     </Box>)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Shivam Mishra