#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. о, неважно, я думаю, это сработает. Да! Спасибо