CrazyTim / spin-wheel

An easy to use, themeable component for randomising choices and prizes.
https://crazytim.github.io/spin-wheel/examples/themes
MIT License
216 stars 59 forks source link

React dependency #4

Open franciszekjob opened 3 years ago

franciszekjob commented 3 years ago

Hey, did you consider creating an equivalent in React? I think it would be veery useful for many people in the community, because the number of options that you provide here, is ridiculously huge (in a good way)! Really appreciate you job!

CrazyTim commented 3 years ago

@Fiiranek thanks for the kind words!

Thats a good idea. It shouldn't be too hard to build a wrapper for React.

Mal1t14 commented 1 year ago

Any progress on this?

richardpeng commented 1 year ago

Here's a stripped down version of how I integrated this with React:

'use client'

import {useState, useRef, useEffect} from "react";
// @ts-ignore
import {Wheel} from 'spin-wheel/dist/spin-wheel-esm'

interface WheelItem {
  label: string
}

interface SpinWheel {
  spinToItem: (
    itemIndex: number,
    duration?: number,
    spinToCenter?: boolean,
    numberOfRevolutions?: number,
    easingFunction?: null | ((t: number) => number)
  ) => void
}

const randomizeNumber = (number: number) => Math.floor(Math.random() * number);

const Home = () => {
  const wheelItems: WheelItem[] = [
    {
      label: 'a'
    },
    {
      label: 'b'
    },
    {
      label: 'c'
    }
  ]
  const container = useRef(null)
  const [mounted, setMounted] = useState(false)
  const [wheel, setWheel] = useState<SpinWheel>()
  useEffect(() => {
    if (!container) return
    console.log('render wheel')
    setWheel(new Wheel(container.current, {
      items: wheelItems,
    }))
    setMounted(true)
  }, [])
  return <div style={{display: 'flex', flexDirection: 'column', height: '100%'}}>
    <div style={{flexDirection: 'row', justifyContent: 'space-between', flex: 0, margin: '12px'}}>
      <button
        onClick={() => {
          wheel!.spinToItem(randomizeNumber(wheelItems.length), 4000, true, 5)
        }}
        disabled={!mounted}
      >Randomize!
      </button>
    </div>
    <div id="wheel" ref={container} style={{
      width: '100vw',
      height: '100vw',
      overflow: 'hidden'
    }}></div>
  </div>
}

export default Home
vandercloak commented 10 months ago

If its useful to anyone else, I implemented it as a hook:

In order for it to work with typescript, you will need to grab the typescript types from this convo: https://github.com/CrazyTim/spin-wheel/issues/23

import { Wheel, WheelProps } from "spin-wheel";
import { useEffect, useMemo, useRef, useState } from "react";

interface UseWheelResult {
  wheel: Wheel | null;
  wheelComponent: JSX.Element;
}

export function useWheel({
  initialProps: _initialProps,
}: {
  initialProps: WheelProps;
}): UseWheelResult {
  const wheelRef = useRef(null);

  const [wheel, setWheel] = useState<Wheel | null>(null);
  const [initialProps] = useState(_initialProps);

  useEffect(() => {
    if (!wheelRef || wheel) return;
    setWheel(new Wheel(wheelRef.current as any, initialProps));
  }, [initialProps, wheelRef, wheel]);

  const wheelComponent = useMemo(() => {
    return (
        <div
          ref={wheelRef}
          style={{
            width: "100%",
            height: "100%",
          }}
        />
    );
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return {
    wheel,
    wheelComponent,
  };
}

Usage:

import { FC } from "react";
import { useWheel } from "./use-wheel";
import { Wheel } from "spin-wheel";

const randomizeNumber = (number: number) => Math.floor(Math.random() * number);

const wheelItems: Wheel["items"] = [
  {
    label: "a",
  },
  {
    label: "b",
  },
  {
    label: "c",
  },
];

const ElementWheel: FC = () => {
  const { wheel, wheelComponent } = useWheel({
    initialProps: { items: wheelItems },
  });

  return (
    <div className="h-full w-full">
      <div className="w-full h-full">
        <button
          className="absolute top-0 left-0 z-10"
          onClick={() => {
            wheel!.spinToItem(
              randomizeNumber(wheelItems.length),
              4000,
              true,
              5
            );
          }}
        >
          Spin!
        </button>
      </div>
      <div className="w-full h-full absolute top-0 left-0">
        {wheelComponent}
      </div>
    </div>
  );
};

export default ElementWheel;
YusufcanY commented 4 months ago

If its useful to anyone else, I implemented it as a hook:

In order for it to work with typescript, you will need to grab the typescript types from this convo: #23

import { Wheel, WheelProps } from "spin-wheel";
import { useEffect, useMemo, useRef, useState } from "react";

interface UseWheelResult {
  wheel: Wheel | null;
  wheelComponent: JSX.Element;
}

export function useWheel({
  initialProps: _initialProps,
}: {
  initialProps: WheelProps;
}): UseWheelResult {
  const wheelRef = useRef(null);

  const [wheel, setWheel] = useState<Wheel | null>(null);
  const [initialProps] = useState(_initialProps);

  useEffect(() => {
    if (!wheelRef || wheel) return;
    setWheel(new Wheel(wheelRef.current as any, initialProps));
  }, [initialProps, wheelRef, wheel]);

  const wheelComponent = useMemo(() => {
    return (
        <div
          ref={wheelRef}
          style={{
            width: "100%",
            height: "100%",
          }}
        />
    );
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return {
    wheel,
    wheelComponent,
  };
}

Usage:

import { FC } from "react";
import { useWheel } from "./use-wheel";
import { Wheel } from "spin-wheel";

const randomizeNumber = (number: number) => Math.floor(Math.random() * number);

const wheelItems: Wheel["items"] = [
  {
    label: "a",
  },
  {
    label: "b",
  },
  {
    label: "c",
  },
];

const ElementWheel: FC = () => {
  const { wheel, wheelComponent } = useWheel({
    initialProps: { items: wheelItems },
  });

  return (
    <div className="h-full w-full">
      <div className="w-full h-full">
        <button
          className="absolute top-0 left-0 z-10"
          onClick={() => {
            wheel!.spinToItem(
              randomizeNumber(wheelItems.length),
              4000,
              true,
              5
            );
          }}
        >
          Spin!
        </button>
      </div>
      <div className="w-full h-full absolute top-0 left-0">
        {wheelComponent}
      </div>
    </div>
  );
};

export default ElementWheel;

It's working great but i have a problem. I'm using React Strict mode and that is causing rendering wheel twice. How can fix that?

Y2KForever commented 1 month ago

It's working great but i have a problem. I'm using React Strict mode and that is causing rendering wheel twice. How can fix that?

React strict is suppose to make the component run twice in development mode, it's there to help you discover side-effects that might not show on initial render.

YusufcanY commented 1 month ago

It's working great but i have a problem. I'm using React Strict mode and that is causing rendering wheel twice. How can fix that?

React strict is suppose to make the component run twice in development mode, it's there to help you discover side-effects that might not show on initial render.

Of course I know that. I don't want to disable Strict mode but I need to prevent that from that happening. I don't want 2 spin wheels 😅