How can i expose my state and state setter method outside without passing props or refs

50 views Asked by At

I was using a library known as Swiper js. In there i saw something which i am unable to understand. Swiper is giving me a hook which returns all the data related to its state like activeSlide, isLast, isStart and many more without explicitly passing ref to that component.

// import Swiper core and required modules
import { Navigation, Pagination, Scrollbar, A11y } from 'swiper/modules';

import { Swiper, SwiperSlide } from 'swiper/react';

// Import Swiper styles
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
import 'swiper/css/scrollbar';

export default () => {
  return (
    <Swiper
      modules={[Navigation, Pagination, Scrollbar, A11y]}
      spaceBetween={50}
      slidesPerView={3}
      navigation
      pagination={{ clickable: true }}
      scrollbar={{ draggable: true }}
      onSwiper={(swiper) => console.log(swiper)}
      onSlideChange={() => console.log('slide change')}
    >
      <SwiperSlide>Slide 1</SwiperSlide>
      <SwiperSlide>Slide 2</SwiperSlide>
      <SwiperSlide>Slide 3</SwiperSlide>
      <SwiperSlide>Slide 4</SwiperSlide>
      ...
    </Swiper>
  );
};
import { useSwiper } from 'swiper/react';

export default function SlideNextButton() {
  const swiper = useSwiper();

  return (
    <button onClick={() => swiper.slideNext()}>Slide to the next slide</button>
  );
}

As you can see above useSwiper returns an object swiper which has a method slideNext . I want to create a component in a similar manner where i can access the state and update the state from outside without passing any props or refs just how swiper.js is doing.

I have tried to use context and importing the context but it didn't work.

1

There are 1 answers

0
Xiduzo On BEST ANSWER

You can create your own context which you can use in all child components.

Create a context

// MySwiper.js
import { createContext, useContext, useState, useCallback } from "react";

const MySwiperContext = createContext();

export const MySwiper = ({ children }) => {
  const [value, setMyValue] = useState(10);

  const myCustomMethod = useCallback(() => {
    // Do things
    console.log("my custom method");
  }, []);

  const overrideValue = useCallback((newValue) => {
    setMyValue(newValue);
  }, []);

  return (
    <MySwiperContext.Provider
      // Things you want to expose to your custom hook
      value={{
        value,
        myCustomMethod,
        overrideValue,
      }}
    >
      {children}
    </MySwiperContext.Provider>
  );
};

// Expose the context with a custom hook
export const useMySwiper = () => useContext(MySwiperContext);

Use the context

// App.js
import "./styles.css";
import { MySwiper, useMySwiper } from "./MySwiper";

// Needs to be a child of the `MySwiper` component
// for the `useMySwiper` hook to work
const MySwiperChildComponent = () => {
  const { value, overrideValue, myCustomMethod } = useMySwiper();

  return (
    <div>
      value from mySwiper {value}
      <br />
      <button onClick={() => overrideValue(value + 10)}>add 10</button>
      <br />
      <button onClick={() => myCustomMethod()}>trigger custom method</button>
    </div>
  );
};

export default function App() {
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen test!</h2>
      <MySwiper>
        <MySwiperChildComponent />
      </MySwiper>
    </div>
  );
}

Check out this code sandbox for a working example