spin-wheel
spin-wheel copied to clipboard
React dependency
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!
@Fiiranek thanks for the kind words!
Thats a good idea. It shouldn't be too hard to build a wrapper for React.
Any progress on this?
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
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;
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?