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)
          }
        }
      }
    }
  }

Relaterte artikler