ADD_SONG не добавлен в плейлист

В моем приложении есть компонент, который отображает (воспроизводит) списки (у меня есть 2 жестко закодированных списка). Я могу добавить новый список в список списков. При нажатии на список отображается список песен, а в нижней части списка находится кнопка, которая, когда вы щелкаете по ней, отображает форму с входными данными (название, исполнитель, альбом). До того, как я исправил функцию добавления списка, песни были добавлены в "активный" список, но теперь действие отправляется (ADD_SONG) и отображается с правильными значениями в состоянии (Redux), но оно отображает элемент / компонент того же типа, что и список и не добавляется / не добавляется... Я не уверен, где искать, я надеюсь, что кто-то может обнаружить мою неисправную логику

AddSongForm

export default class AddSongForm extends React.PureComponent {
  constructor() {
      super();

      this.state = {
        clicked: false
      };

      this.handleClick = this.handleClick.bind(this)
    }

    handleClick() {
      this.setState({
        clicked: !this.state.clicked
      })
    }

  handleChange = (event) => {
    const value = event.target.value
    const name = event.target.name
// console.log(name, value)
// console.log(this.state);
    this.setState({
      [name]: value
    })
  }

  handleSubmit = (event) => {
    event.preventDefault()
console.log(this.state);
    if (this.state.title && this.state.artist) {
      this.props.addSong({
        title: this.state.title,
        artist: this.state.artist,
        album: this.state.album
      })
    }
  }

  render() {

    return (<div>
      <button onClick={this.handleClick}><h2>New Song+</h2></button>

  {this.state.clicked ?
     <form onSubmit={this.handleSubmit}>
        <label>
          Song Title:
          <input type="text" name="title" onChange={this.handleChange} />
        </label>
        <label>
        <br/>  Artist:
          <input type="text" name="artist" onChange={this.handleChange} />
        </label>
        <label>
        <br/>  Album:
          <input type="text" name="album" onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
      : null}
    </div>)
  }

}

AddSongFormContainer

import AddSongForm from './AddSongForm'
import { connect } from 'react-redux'

class AddSongFormContainer extends React.PureComponent {
  addSong = (song) => {
    this.props.dispatch({
      type: 'ADD_SONG',
      payload: {
        id: Math.ceil(Math.random()*10000),
        ...song
      }
    })
  }

  render() {

    return <AddSongForm addSong={this.addSong} />
  }
}

export default connect(null)(AddSongFormContainer)

Редуктор с начальным состоянием

const initState = [

  {
    id: 1,
    title: 'Play list 1',
    data: [
      {
        id: 1,
        title: 'DogHeart II',
        artist: 'The Growlers',
        album: 'Gilded Pleasures'
      }, {
        id: 2,
        title: 'Beast of No nation',
        artist: 'Fela Kuti',
        album: 'Finding Fela'
      }, {
        id: 3,
        title: 'Satellite of love',
        artist: 'Lou Reed',
        album: 'Transformer'
      }
    ]
  }, {
    id: 2,
    title: 'Play list 2',
    data: [
      {
        id: 1,
        title: 'Whatever happend to my Rock and Roll',
        artist: 'BlackRebelMoterCycleClub',
        album: 'B.R.M.C'
      }, {
        id: 2,
        title: 'U Sexy Thing',
        artist: 'Crocodiles',
        album: 'CryBaby Demon/ U Sexy Thing'
      }, {
        id: 3,
        title: 'Oh Cody',
        artist: 'NoBunny',
        album: 'Raw Romance'
      }
    ]
  }
]
const reducer = (state = initState, action = {}) => {
  switch (action.type) {

    case 'ADD_LIST':
      return [
        ...state,
        action.payload
      ]
    case 'ADD_SONG':
      return [
        ...state,
        action.payload
      ]

    default:
      return state
  }
}

export default reducer

Компонент PlayList сопоставляет все песни в списке

export default class PlayList extends React.Component{
  constructor() {
      super()

      this.state = {
        clicked: false
      }
      this.handleClick = this.handleClick.bind(this);
    }

    handleClick() {
      this.setState({
        clicked: !this.state.clicked
      })
      console.log(this.props.selectList.name)

    }


render(){
  // console.log(this.props);
// console.log(this.props.playLists[0].data);
  return (<div>
    <button  onClick={this.handleClick}><h1>{this.props.playLists.title}</h1></button>
{this.state.clicked ?
  <ul>
      { this.props.playLists.data.map(song =>
        <li key={song.id} onClick={() => this.props.selectSong(song.id)}>
          <b>{song.title}</b><br/>
            By:
            <br/>
            <h3><b>{song.artist}</b></h3><br/>
             Appears on:
              <b>{song.album}</b><br/><br/>
        </li>
      ) }
      <AddSongFormContainer/>
    </ul>


     : null}


  </div>)
  }
}

Контейнер для всех списков воспроизведения в исходном состоянии (массив)

class PlayListsContainer extends React.PureComponent {


  selectSong = (id) => {
  this.props.dispatch({
    type: 'SELECT_SONG',
    payload: id
  })
}

  selectSong(id) {
    console.log('selected song:', id)
  }
  selectList = (id) => {
    this.props.dispatch({
      type: 'SELECT_LIST',
      payload: id
    })
  }

    selectList(id) {
      console.log('selected song:', id)
    }

    render() {
        const playlistsArray = this.props.playLists
        // console.log(playlistsArray)
        return (
            playlistsArray.map((playlist) => <PlayList
                playLists={playlist}
                selectSong={this.selectSong}
                selectList={this.selectList}
                key={playlist.id}
            />)

        )
    }








}

const mapStateToProps = (state) => {
// console.log(state.playLists);
  return {
    playLists: state.playLists



  }
}

export default connect(mapStateToProps)(PlayListsContainer)

состояние редукса после отправки новой песни

1 ответ

Решение

С вашим комментарием, описывающим ваши проблемы с редуксом, и скриншотом инструментов разработчика Redux - проблема теперь ясна.

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

Было бы вполне возможно исправить это как есть. В вашем редукторе, вместо того, чтобы добавлять песню, как вы делаете сейчас, вам нужно добавить ее специально в плейлист. Если вам нужен пример кода, я могу привести его.

Тем не менее, я призываю вас провести реструктуризацию вашего редукционного магазина - и придерживаться лучших практик нормализованного плоского состояния.

Это означает, что вы хотите иметь два объекта верхнего уровня для вашего магазина.

playlists
songs

Вместо того, чтобы включать все данные о песне в список воспроизведения, вы просто ссылаетесь на идентификатор песни.

Ваши плейлисты будут выглядеть так:

playlists { 
  1: {
  title: 'my playlist'
  songs: [1,2,3]}

И песни могут остаться прежними.

Всякий раз, когда вы добавляете песню в список воспроизведения, вы просто добавляете песню, а затем обновляете список воспроизведения новым идентификатором песни.

Другая практика, которую вы можете сделать, чтобы сделать ваш код немного чище, - это использовать mapDispatchToProps, а не определять свои встроенные действия по редуксу. Документы для этого здесь.

#

Чтобы исправить код как есть, главное, что нам нужно сделать, это передать плейлист, в который вы хотите добавить песню. В противном случае, как еще мы узнаем, куда поместить песню?

Во-первых, обновите ваше действие в addSongFormContainer, чтобы принять дополнительный аргумент targetPlaylist (в который войдет песня)

  addSong = (song, targetPlaylist) => {
this.props.dispatch({
  type: 'ADD_SONG',
  payload: {
    playlist: targetPlaylist
    id: Math.ceil(Math.random()*10000),
    ...song
    }
  })
}

Использование этого действия теперь требует, чтобы вы передавали целевой список воспроизведения. Для краткости я собираюсь жестко закодировать, что песня добавляется в список воспроизведения 1. Я оставлю упражнение по передаче выбранного списка воспроизведения до компонента на ваше усмотрение.

Я очистил handleSubmit, чтобы сделать его более понятным, переместив песню в ее собственную переменную.

  handleSubmit = (event) => {
  event.preventDefault()
  console.log(this.state);

  if (this.state.title && this.state.artist) {

    let song = {
      title: this.state.title,
      artist: this.state.artist,
      album: this.state.album
    }
    let selectedPlayList = 1 //Fix this later :)
    this.props.addSong(song, selectedPlayList)
  }
}

Теперь последняя проблема - это редуктор.

case 'ADD_SONG':
      const index = store.getState().findIndex(playlist => playlist.id === 
        action.payload.playlist)
      console.log(index) //This should be index 0, after looking up playlist id: 1
      const updatedPlaylistSongs = this.state[index].data
      updatedPlaylistSongs.push(action.playload.song)
      return [
        ...state.slice(0, index), // All playlists before current
        {
          ...state[index], //The targeted playlist.
          ...updatedPlaylistSongs //The updated songs
        },
        ...state.slice(index + 1), //All playlists after current
    ]

Я надеюсь, что редуктор работает для вас, хотя может потребоваться немного работы - я не привык писать редукторы, работающие с массивами. У меня обычно есть нормализованные данные, что приводит к гораздо более легкой модификации. Я настоятельно рекомендую вам попытаться нормализовать ваш магазин. Прекратите использовать массивы, попробуйте использовать объекты, где генерируется ключ (используйте uuidv4 для создания уникального и случайного ключа). Это значительно упрощает "выбор" того, что вы хотите редактировать / обновлять.

Надеюсь, это поможет!

Другие вопросы по тегам