簡體   English   中英

即使在 Redux 減速器中復制 state 后,React 組件也不會更新

[英]React Component not updating even after duplication of state in Redux reducer

語境

目標是當我按下特定鍵時,在 App.js 中反應呈現一個鍵名的組件,並在另一個組件中注冊。 該信息正在通過 redux 管理的 state 傳遞。

問題

這很簡單:我正在我的 redux 減速器中更新我的 state,但即使在復制它時(由於 redux 和我的開發工具允許我看到不同的下一個狀態,我可以看到它)

問題很簡單:

為什么即使在連接並復制了我的 state 之后,我的 App.js 組件也不會重新渲染?

我想我確保我的 state 與傳播操作重復,我的 redux 開發工具向我展示了一個很好的 state 更新,而沒有我的 prevState 和 nextState 重復。 我瀏覽了很多帖子,發現只有那些忘記在他們的減速器中復制他們的 state 的人,而我沒有。 那么這里有什么問題??

開發工具示例

在此處輸入圖像描述

代碼

這是代碼,很簡單。 有趣的部分是playSoundplayKeys

應用程序.js:

import React from 'react'
import './App.css';
import { connect } from 'react-redux';

import KeyComponent from './Components/Key'
import SoundPlayer from './Components/Sounds'

const mapStateToProps = (state) => ({
  ...state.soundReducer
 })

class App extends React.Component {
  constructor(props) {
    super(props);
  }
  
  render(){
    return (
      <div>
      {console.log(this.props)}
        {
          this.props.playedKeys.map(key =>{
            <KeyComponent keyCode={key}>  </KeyComponent>
          })
        }
        <SoundPlayer></SoundPlayer>
      </div>
    );
  }
}

export default connect(mapStateToProps)(App);

減速器

export default (state = {allSounds:{},playedKeys:[]}, action) => {
  switch (action.type) {
    case 'ADD_SOUND':
      return reduce_addSound({...state},action)
    case 'PLAY_SOUND':
      return reduce_playSound({...state,playedKeys : [...state.playedKeys]},action)
    
    default:
    return state
  }
}
  
function reduce_addSound (state,action){

  let i = 0
  state.allSounds[action.payload.key] = { players : new Array(5).fill('').map(()=>(new Audio())) , reader : new FileReader()}

  //load audioFile in audio player
    state.allSounds[action.payload.key].reader.onload = function(e) {
      state.allSounds[action.payload.key].players.forEach(player =>{
        player.setAttribute('src', e.target.result);
        player.load();
        player.id = 'test'+e.target.result+ i++ 
      })
  }
  state.allSounds[action.payload.key].reader.readAsDataURL(action.payload.input.files[0]);
  
  return state
}

function reduce_playSound(state,action){

  state.playedKey = action.payload.key;

  if(!state.playedKeys.includes(state.playedKey))
    state.playedKeys.push(action.payload.key);

  return state
}

行動

export const addSound = (key, input,player) => (dispatch,getState) => {
    dispatch({
        type: 'ADD_SOUND',
        payload: {key : key, input : input}
       })
   }
   
export const playSound = (key) => (dispatch,getState) => {
    dispatch({
        type: 'PLAY_SOUND',
        payload: {key : key}
       })
   }

注冊按鍵的組件



import React from 'react'
import { connect } from 'react-redux';

import { playSound } from '../../Actions/soundActions';

const mapStateToProps = (state) => ({
  ...state.soundReducer
 })

 const mapDispatchToProps = dispatch => ({
    playSound: (keyCode) => dispatch(playSound(keyCode))
 })

 class SoundPlayer extends React.Component {

    constructor(props) {
        super(props);
    }
    componentDidMount () {
        this.playSoundComponent = this.playSoundComponent.bind(this)
        document.body.addEventListener('keypress', this.playSoundComponent);
    }

    keyCodePlayingIndex = {};

    playSoundComponent(key){
        if(this.props.allSounds.hasOwnProperty(key.code)){

            if(!this.keyCodePlayingIndex.hasOwnProperty(key.code))
                this.keyCodePlayingIndex[key.code] = 0

            this.props.allSounds[key.code].players[this.keyCodePlayingIndex[key.code]].play()

            this.keyCodePlayingIndex[key.code] = this.keyCodePlayingIndex[key.code] + 1 >= this.props.allSounds[key.code].players.length ? 0 : this.keyCodePlayingIndex[key.code] + 1
            console.log(this.keyCodePlayingIndex[key.code])
        }

        this.props.playSound(key.code);
    }

    render(){
        return <div>
            <h1 >Played : {this.props.playedKey}</h1>
            {Object.keys(this.keyCodePlayingIndex).map(key =>{
                return <p>{key} : {this.keyCodePlayingIndex[key]}</p>
            })}
        </div>
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(SoundPlayer);

問題

你正在改變你的 state object。

  • state.allSounds[action.payload.key] =...
  • state.playedKey = action.payload.key;

解決方案

更新你的 reducer 函數以返回新的state對象,記住正確淺拷貝正在更新的每個深度級別。

export default (state = { allSounds: {}, playedKeys: [] }, action) => {
  switch (action.type) {
    case 'ADD_SOUND':
      return reduce_addSound({ ...state },action);

    case 'PLAY_SOUND':
      return reduce_playSound({ ...state, playedKeys: [...state.playedKeys] }, action);
    
    default:
    return state
  }
}
  
function reduce_addSound (state, action) {
  const newState = {
    ...state,  // shallow copy existing state
    allSounds: {
      ...state.allSounds, // shallow copy existing allSounds
      [action.payload.key]: {
        players: new Array(5).fill('').map(()=>(new Audio())),
        reader: new FileReader(),
      },
    }
  };

  // load audioFile in audio player
  newState.allSounds[action.payload.key].reader.onload = function(e) {
    newState.allSounds[action.payload.key].players.forEach((player, i) => {
      player.setAttribute('src', e.target.result);
      player.load();
      player.id = 'test' + e.target.result + i // <-- use index from forEach loop
    })
  }
  newState.allSounds[action.payload.key]
    .reader
    .readAsDataURL(action.payload.input.files[0]);
  
  return newState;
}

function reduce_playSound (state, action) {
  const newState = {
    ...state,
    playedKey: action.payload.key,
  };

  if(!newState.playedKeys.includes(newState.playedKey))
    newState.playedKeys = [...newState.playedKeys, action.payload.key];

  return newState
}

好的,我明白了,它總是我們不檢查的最簡單最愚蠢的事情。

澄清

So my state was properly duplicated with reduce_addSound({...state },action) and reduce_playSound({...state, playedKeys: [...state.playedKeys] and like I wrote in my question, that wasn't the問題 !

問題

盡其所能,我沒有在我的渲染 function.. 中返回組件:

在 App.js 中:

  render(){
    return (
      <div>
        {
          this.props.soundReducer.playedKeys.map(key =>{
            <KeyComponent keyCode={key}>  </KeyComponent> //<-- NO return or parenthesis !!
          })
        }
        <SoundPlayer></SoundPlayer>
      </div>
    );
  }

回答

App.js 用括號渲染 function:

  render(){
    return (
      <div>
        {
          this.props.soundReducer.playedKeys.map(key =>(
            <KeyComponent key = {key} keyCode={key}>  </KeyComponent> //<-- Here a component is returned..
          ))
        }
        <SoundPlayer></SoundPlayer>
      </div>
    );
  }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM