Похоже, не удается обновить состояние с помощью инструментария Redux

#redux #react-redux #react-hooks #state #redux-toolkit

Вопрос:

Я создаю приложение React, используя Tone.js и инструментарий redux, и у меня возникли проблемы с получением функции для чтения обновленного состояния. Кажется, я могу обновить состояние одним нажатием кнопки, но это не отражено в функции thisWillRepeat ниже.

В приведенном ниже коде мне в идеале нужно Tone.Transport.scheduleRepeat будет запустить только один раз. Tone.Transport.scheduleRepeat принимает точное время запланированного события в аргументе функции обратного вызова, и им можно управлять startStopHandler() . Я попробовал несколько вещей, включая добавление [theState] в качестве второго useEffect аргумента, но это приводит к полному беспорядку, так как запланировано много дополнительных повторов. Я уверен, что есть что-то основное, чего мне не хватает.

Основной компонент приложения

 import React, { useEffect } from 'react'
import * as Tone from 'tone'

import { useSelector, useDispatch } from 'react-redux'
import { songActions } from '../store/index'

const Sequencer = () => {

  const theState = useSelector(state => state.theSong)
  const dispatch = useDispatch()

  const startStopHandler = () => {
    dispatch(songActions.startStopHandler())
    !theState.isPlaying ? Tone.Transport.start() : Tone.Transport.stop()
  }

  const changeTheState = () => dispatch(songActions.updateAbitOfState())

  // why doesn't the state update in the console.log below after updating it successfully with buttons below?
  const thisWillRepeat = () => console.log(theState.drums)

  useEffect(() => {
    Tone.Transport.scheduleRepeat((time) => { 
      thisWillRepeat(time)
    }, '8n')
  },[])

  return <> 
      <button onClick={() => startStopHandler()}>Play / Pause</button>
      <br/>
      A bit of state in the store has the value of: {theState.drums}
      <br/>
      <button onClick={() => changeTheState()}>Change the state</button>
      <button onClick={() => console.log(theState)}>Click to get the state</button>
    </>
}

export default Sequencer
 

Магазин

 import { createSlice, configureStore } from '@reduxjs/toolkit'

const initialSongState = {
  isPlaying: false,
  drums: 'not changed'
}

const songSlice = createSlice({
  name: 'theSong',
  initialState: initialSongState,
  reducers: {
    startStopHandler(state){
      state.isPlaying = !state.isPlaying
    },
    updateAbitOfState(state) {
      state.drums = 'something new!'
    }
  }
})

const store = configureStore({
  reducer: {
    theSong: songSlice.reducer
  }
})

export const songActions = songSlice.actions

export default store
 

Ответ №1:

Вы создали закрытие, которое фиксирует значение theState на момент первого отображения компонента:

const thisWillRepeat = () => console.log(theState.drums)

По определению, он никогда не может указывать на значения из более поздних рендеров.

Чтобы прочитать последнее состояние, вам потребуется либо:

  • получите прямой доступ к магазину и позвоните store.getState() внутри этого обратного вызова
  • Используйте ссылку, назначьте refObj.current = theState другой эффект выше этого, а затем console.log(refObj.current)
  • Переместите логику за пределы компонента в модуль Redux, который также имеет доступ к getState

Смотрите пост Дэна Абрамова «Полное руководство useEffect по тому, как лучше понять закрытие и как useEffect оно взаимодействует с ценностями».

Комментарии:

1. Спасибо, что поделился. Как вы звоните store.getState() внутри обратного вызова? Я изо всех сил старался заставить это работать..

2. о, неважно, я думаю, это сработает. Да! Спасибо