reactjs - React + PIXI + Zustand - Utdatert tilstand i musehåndterere
Jeg har et Pixi-lerret med pekerhåndterere. Du kan klikke på et punkt på en 'sekvens' og flytte det.
Mus-håndtererne hadde utdatert tilstand. For å fikse det oppretter jeg håndtererne på nytt hver gang sekvensen endres, slik at de alltid har fersk tilstand. Dette løste tilstandsfeilen. Men det fungerer ikke hvis tilstanden endres ved mus-oppløft. Å opprette nye håndterere fra innenfor en håndterer bevarer forståelig nok den utdaterte tilstanden, selv via en tilbakeringings-timer. Så nå er alle feilene fikset bortsett fra at du ikke kan dra et punkt to ganger :)
Jeg er ny på React og føler at jeg gjør alt dette helt feil. Har du noen forslag?
Slik får jeg sekvensstatusen:
const SequenceCanvas = React.forwardRef((props: any, ref) => {
const canvasRef = useRef(null)
const sequence: Sequence = useSequenceStore(state => { return state.sequence} )
Her er nå jeg gjenskaper handlerne:
const addHandlers = () : void => {
console.log(`new mouse handlers: numsteps ${sequence.numSteps}`)
parentContainer.removeAllListeners()
parentContainer.on('pointerdown', (e: any) => {
handlePointerDown(e)
})
parentContainer.on('dblclick', (e: any) => {
handleDoubleClick(e)
})
parentContainer.on('pointerup', (e: any) => {
handlePointerUp(e)
})
parentContainer.on('mouseupoutside', (e: any) => {
handlePointerUp(e)
})
parentContainer.on('pointermove', (e: any) => {
handlePointerMove(e)
})
}
useEffect(() => {
if (!mouseIsDown) {
// Recreate mouse handlers each time the sequence changes. Otherwise the sequence state is stale
addHandlers()
}
}, [sequence]);
const handlePointerUp = (e: any) => {
console.log(`handlePointerUp ${mouseIsDown}`)
mouseIsDown = false;
dragStepNum = -1
isDraggingVertical = false;
isDraggingHorizontal = false;
setTimeout(() => {
addHandlers()
}, 100)
}
Her er et eksempel på foreldet tilstand. sequence-objektet her er foreldet med mindre jeg gjenoppretter pekerhåndtereren hver gang sekvensen endres.
const handlePointerMoveImp = (e: any) => {
const sequence = useSequenceStore.getState().sequence
if (mouseIsDown) {
var x = e.data.global.x
var y = e.data.global.y
if (x !== null && y !== null && x !== Infinity && y !== Infinity) {
// console.log(`drag ${JSON.stringify(e.data)}`)
if (dragTarget === DragTargets.EnvelopePoint) {
console.log(`handlePointerMoveImp - drag target is EnvelopePoint - tapPointNum ${tapPointNum}`)
const midiOutputDeviceName : string = sequence.midiSettings.midiOutputDeviceName
const midiChart:MidiChart = findMidiChart(midiOutputDeviceName, midiMaps)
const envelope = findEnvelope(sequence.currentEnvelopeId);
if (envelope) {
// const point = envelope.points[tapPointNum]
const controllerInfo: ControllerInfo = MidiDeviceDataService.getControllerInfo(midiChart, envelope.controller)
const time = Math.max(getXTime(sequence, x), 0)
const value: number = getYValue(sequence, y, controllerInfo.min, controllerInfo.max)
console.log(`handlePointerMoveImp - drag EnvelopePoint to ${time}/${value} - envelope <${sequence.currentEnvelopeId}>`)
moveEnvelopePoint(sequence.currentEnvelopeId, tapPointNum, time, value)
}
}
Svar #1
Utdatert React-tilstand i tilbakeringingshåndterere blir vanligvis fikset ved å lagre en kopi av den nåværende tilstanden i en React-ref, og referere til refens nåværende verdi i tilbakekallene. Jeg er ikke veldig kjent med zustand, men jeg tror noe som det følgende bør være veldig nær det du er ute etter.
const SequenceCanvas = React.forwardRef((props: any, ref) => {
const canvasRef = useRef(null);
const sequence: Sequence = useSequenceStore(state => state.sequence);
const sequenceRef = useRef<Sequence>(sequence);
useEffect(() => {
sequenceRef.current = sequence;
}, [sequence]);
...
useEffect(() => {
parentContainer.on('pointerdown', handlePointerDown);
parentContainer.on('dblclick', handleDoubleClick);
parentContainer.on('pointerup', handlePointerUp);
parentContainer.on('mouseupoutside', handlePointerUp);
parentContainer.on('pointermove', handlePointerMove);
return () => {
parentContainer.removeAllListeners()
};
}, []);
...
const handlePointerUp = (e: any) => {
console.log(`handlePointerUp ${mouseIsDown}`)
mouseIsDown = false;
dragStepNum = -1
isDraggingVertical = false;
isDraggingHorizontal = false;
}
...
const handlePointerMoveImp = (e: any) => {
const sequence = sequenceRef.current;
if (mouseIsDown) {
const { x, y } = e.data.global.x;
if (x !== null && y !== null && x !== Infinity && y !== Infinity) {
if (dragTarget === DragTargets.EnvelopePoint) {
const midiOutputDeviceName : string = sequence.midiSettings.midiOutputDeviceName
const midiChart:MidiChart = findMidiChart(midiOutputDeviceName, midiMaps)
const envelope = findEnvelope(sequence.currentEnvelopeId);
if (envelope) {
const controllerInfo: ControllerInfo = MidiDeviceDataService.getControllerInfo(midiChart, envelope.controller)
const time = Math.max(getXTime(sequence, x), 0)
const value: number = getYValue(sequence, y, controllerInfo.min, controllerInfo.max)
moveEnvelopePoint(sequence.currentEnvelopeId, tapPointNum, time, value)
}
}
}
}
}
