我有一个React App,当它感测到API命中(使用Pusher进行发布-订阅)时,它会传递到SlotMachine
组件。
每个API命中应该rotate()
一次,但是,正如您在我的输出中看到的那样,它会调用rotate()
多次次,并说道具每次都变了。但是道具在控制台中看起来是一样的,并且在最初的道具之后没有更多的API命中(我使用Postman触发)。
任何原因让我的道具发生很大变化?我对钩子或道具不了解? 另外,为什么我的Rotate函数总是不断被调用,除非在第一个API命中之后?
应用程序:
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'));
老虎机:
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
输出:
初始API命中(按预期工作,仅触发一次Rotate()
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
随后的API匹配会进行多次轮换调用,并显示道具在每次轮换之前都会发生变化。
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
答案 0 :(得分:3)
我认为问题是由于您每次渲染应用程序时都创建了Pusher。我认为,将代码移到您的应用之外可以解决问题。 更新:在应用就绪后注册事件
const pusher = new Pusher('XXXXXXX', {
cluster: 'XXX',
encrypted: true
});
const App = () => {
const [ slots, setSlots ] = useState([]);
const [ apiHit, setApiHit ] = useState(false);
useEffect(() => {
const channel = pusher.subscribe('my-channel');
const handler = data => {
console.log(`Got data from pusher ${data}`);
setSlots(data);
setApiHit(true);
};
channel.bind('my-event', handler);
return () => {
channel.unbind(null, handler);
channel.unsubscribe();
}
} ,[]);
return(
<div>
<SlotMachine slots={slots} apiHit={apiHit}/>
</div>
);
};
答案 1 :(得分:1)
如果要减少多个渲染,请将多个状态组合到一个对象中并更新相应的键
例如
// const [manual, setManual] = useState(true);
// const [rings, updateRings] = useState([]);
// const [ slots, setSlots ] = useState([]);
const [state, setState] = useState({ manual: true, rings: [], slots: [] });
and in your effects, code in a way to create and update the state and finally set it once.
useEffect(()=>{
console.log(' --- props change --- ');
console.log(props);
const newState = { ...state };
newState.slots = props.slots;
if(props.slots !== undefined && props.apiHit === true ) {
newState.manual = false;
setState(newState);
rotate();
setState({ ...state, manual: true });
}
}, [props.slots, props.apiHit]);