I have a react App that passes to a SlotMachine
component when it senses an API hit (using Pusher for publish-subscribe).
It should only rotate()
once per API hit, however, as you can see in my output it invokes rotate()
multiple times and says that props have changed before each time. BUT the props look the same in the console and there are not more API hits after the initial one (I'm triggering using Postman).
Any reason for my props to change so much? Something I don't understand about hooks or props? Also, why does my rotate function constantly get called except after the first API hit?
App:
import React, { useState, useEffect } from 'react'
import { render } from 'react-dom'
import Pusher from 'pusher-js'
import SlotMachine from './components/SlotMachine'
const App = () => {
const [ slots, setSlots ] = useState([]);
const [ apiHit, setApiHit ] = useState(false);
const pusher = new Pusher('XXXXXXX', {
cluster: 'XXX',
encrypted: true
});
const channel = pusher.subscribe('my-channel');
channel.bind('my-event', data => {
console.log(`Got data from pusher ${data}`);
setSlots(data);
setApiHit(true);
});
return(
<div>
<SlotMachine slots={slots} apiHit={apiHit}/>
</div>
);
};
render(<App />, document.getElementById('root'));
Slots Machine:
import React, { useEffect, useRef, useState } from 'react'
import './slots.css'
function rnd(min, max) {
return Math.floor(Math.random() * (max - min)) + min
}
const SlotMachine = (props) => {
const ringElements = useRef();
const [manual, setManual] = useState(true);
const [rings, updateRings] = useState([]);
const [ slots, setSlots ] = useState([]);
const slotMap = {
0: 72,
1: 144,
2: 216,
3: 288,
4: 360
};
useEffect(()=>{
console.log(' --- props change --- ');
console.log(props);
setSlots( slots => props.slots );
if(props.slots !== undefined && props.apiHit === true ) {
setManual(manual => false);
rotate();
setManual(true);
}
}, [props.slots, props.apiHit]);
//need to store previous to get accurate measure
const rotate = () => {
console.log(`---ROTATE---`);
rings.forEach((el, i) => {
let obj = el; //should be an obj
console.log(obj);
let prev = null;
if (obj.prev !== null) { prev = obj.prev; } //update prev if not first time
console.log(`Prev is ${prev}`);
let element = obj.ringElement; //should be the ring itself
//let obj = rings[i];
console.log(element);
//get the slots it needs to move to
let move = manual && !props.apiHit ? slotMap[rnd(0,4)] : slotMap[slots[i]];
console.log(`move: ${move}`);
let diff = Math.abs(prev - move);
console.log(`The diff is ${diff}`);
//take the previous and the move and figure out "difference", be sure to add that difference to the spin
let res = move + (360 * rnd(3,6));
//let res = move //+ (360 * rnd(3,6)); //randomly spin should end up back at same spot no matter what
console.log(`rotateX(${res}deg) for ring${i}`);
element.style.transform = `rotateX(${res}deg)`;
obj.prev = move;
//rings[i] = obj;// update proper object in state
rings[i] = obj;
updateRings( rings );
});
}
//when first mounts
useEffect(() => {
console.log('component did mount');
Array.from(ringElements.current.children).forEach((el, i) => {
console.log(`elements in ring: ${el}`);
let obj = {}
obj.ringElement = el
obj.prev = null
updateRings(rings => [...rings, obj])
// updateRings(rings => [...rings, el]);
});
}, []);
return(
<div>
<div className="slots">
<div className="rings" ref={ringElements}>
<div className="ring">
<div className="slot" id="0">0</div>
<div className="slot" id="1">1</div>
<div className="slot" id="2">2</div>
<div className="slot" id="3">3</div>
<div className="slot" id="4">4</div>
</div>
<div className="ring">
<div className="slot" id="0">0</div>
<div className="slot" id="1">1</div>
<div className="slot" id="2">2</div>
<div className="slot" id="3">3</div>
<div className="slot" id="4">4</div>
</div>
<div className="ring">
<div className="slot" id="0">0</div>
<div className="slot" id="1">1</div>
<div className="slot" id="2">2</div>
<div className="slot" id="3">3</div>
<div className="slot" id="4">4</div>
</div>
</div>
</div>
<button className="spin-button" onClick={() => { setManual(true); rotate(); } }>SPIN</button>
</div>
);
}
export default SlotMachine
Output:
initial API Hit (works as expected, triggers Rotate() exactly once
Got data from pusher 3,1,3 . <-- new API data recieved
55 **--- props change ---**
56 {slots: Array(3), apiHit: false}
55 **--- props change ---**is
56 {slots: Array(3), apiHit: true}
71 **---ROTATE---**
75 {ringElement: div.ring, prev: null}
83 Prev is null
87 <div class="ring" style="transform: rotateX(2088deg);">…</div>
90 move: 288
92 The diff is 288
96 rotateX(2088deg) for ring0
75 {ringElement: div.ring, prev: null}
83 Prev is null
87 <div class="ring" style="transform: rotateX(1224deg);">…</div>
90 move: 144
92 The diff is 144
96 rotateX(1224deg) for ring1
75 {ringElement: div.ring, prev: null}
83 Prev is null
87 <div class="ring" style="transform: rotateX(2088deg);">…</div>
90 move: 288
92 The diff is 288
96 rotateX(2088deg) for ring2
Subsequent API hits do multiple rotate calls and show props changing before each one.
Got data from pusher 3,0,1 . <-- new API hit
55 --- props change ---
56 {slots: Array(3), apiHit: true}
71 ---ROTATE---
75 {ringElement: div.ring, prev: 288}
83 Prev is 288
87 <div class="ring" style="transform: rotateX(2088deg);">…</div>
90 move: 288
92 The diff is 0
96 rotateX(1368deg) for ring0
75 {ringElement: div.ring, prev: 144}
83 Prev is 144
87 <div class="ring" style="transform: rotateX(1224deg);">…</div>
90 move: 144
92 The diff is 0
96 rotateX(1584deg) for ring1
75 {ringElement: div.ring, prev: 288}
83 Prev is 288
87 <div class="ring" style="transform: rotateX(2088deg);">…</div>
90 move: 288
92 The diff is 0
96 rotateX(2088deg) for ring2
VM12097 index.js:39 Got data from pusher 3,0,1 . <-- **same** API hit but recorded again????
55 --- props change ---
56 {slots: Array(3), apiHit: true}apiHit: trueslots: (3) [3, 0, 1]__proto__: Object
71 ---ROTATE---
75 {ringElement: div.ring, prev: 288}
83 Prev is 288
87 <div class="ring" style="transform: rotateX(2088deg);">…</div>
90 move: 288
92 The diff is 0
96 rotateX(1368deg) for ring0
75 {ringElement: div.ring, prev: 144}
83 Prev is 144
87 <div class="ring" style="transform: rotateX(1224deg);">…</div>
90 move: 72
92 The diff is 72
96 rotateX(1152deg) for ring1
75 {ringElement: div.ring, prev: 288}
83 Prev is 288
87 <div class="ring" style="transform: rotateX(2088deg);">…</div>
90 move: 144
92 The diff is 144
96 rotateX(1584deg) for ring2
VM12097 index.js:39 Got data from pusher 3,0,1
55 --- props change ---
56 {slots: Array(3), apiHit: true}
71 ---ROTATE---
75 {ringElement: div.ring, prev: 288}
83 Prev is 288
87 <div class="ring" style="transform: rotateX(2088deg);">…</div>
90 move: 288
92 The diff is 0
96 rotateX(1728deg) for ring0
75 {ringElement: div.ring, prev: 72}
83 Prev is 72
87 <div class="ring" style="transform: rotateX(1224deg);">…</div>
90 move: 72
92 The diff is 0
96 rotateX(1152deg) for ring1
75 {ringElement: div.ring, prev: 144}
83 Prev is 144
87 <div class="ring" style="transform: rotateX(2088deg);">…</div>
90 move: 144
92 The diff is 0
96 rotateX(1224deg) for ring2