CrazyTim / spin-wheel

An easy to use, themeable component for randomising choices and prizes.
https://crazytim.github.io/spin-wheel/examples/themes
MIT License
167 stars 41 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 12 months 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 7 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 1 month 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?