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 для создания уникального и случайного ключа). Это значительно упрощает "выбор" того, что вы хотите редактировать / обновлять.
Надеюсь, это поможет!