即使在 Redux reducer 中复制状态后,React 组件也不会更新

时间:2021-01-02 23:29:20

标签: javascript reactjs react-redux state

背景

目标是当我按下一个特定的键时,在 App.js 中反应渲染一个键名的组件,在另一个组件中注册。信息通过 redux 管理状态传递。

问题

很简单: 我正在我的 redux reducer 中更新我的状态,但即使在复制它时(我可以看到它,这要归功于 redux dev 工具,它允许我观察我的 prevState 和我的 nextState 不同)

问题很简单:

<块引用>

为什么我的 App.js 组件即使在连接和 复制我的状态?

我想我确保通过传播操作复制了我的状态,并且我的 redux 开发工具显示了一个很好的状态更新,而不会复制我的 prevState 和 nextState。我浏览了很多帖子,发现只有那些忘记在他们的减速器中复制他们的状态的人,而我没有。 那么这里有什么问题??

开发工具示例

enter image description here

代码

这是代码,很简单。有趣的是playSoundplayedKeys

App.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);

2 个答案:

答案 0 :(得分:1)

问题

你正在改变你的状态对象。

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

答案 1 :(得分:1)

好吧,我明白了,这总是最简单最愚蠢的事情,我们不检查哈。

澄清

所以我的状态被 reduce_addSound({ ...state },action)reduce_playSound({ ...state, playedKeys: [...state.playedKeys] 正确复制,就像我在问题中写的那样,这不是问题!

问题

尽其所能,我没有在渲染函数中返回组件.. :

在 App.js 中:

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

答案

带括号的 App.js 渲染函数:

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