Skip to content

Instantly share code, notes, and snippets.

@justingreenberg
Last active February 29, 2020 04:21
Show Gist options
  • Save justingreenberg/79ca0b4bc374a5485666e3e2f8d2d7d1 to your computer and use it in GitHub Desktop.
Save justingreenberg/79ca0b4bc374a5485666e3e2f8d2d7d1 to your computer and use it in GitHub Desktop.
import React from 'react'
import ReactDOM from 'react-dom'
import { createStore } from 'redux'
import { connect, Provider } from 'react-redux'
import './styles.css'
// Log unnecessary re-renders
require('@welldone-software/why-did-you-render')(React, {
trackAllPureComponents: true,
include: [/^ConnectFunction/],
})
console.clear()
// Given normalized data
const initialState = {
current: null,
ui: { playlist: null },
cursor: { playlist: null, index: null },
songs: {
1: { id: 1, artist: 'Wu-Tang', title: 'Triumph' },
2: { id: 2, artist: 'The Offspring', title: 'Self Esteem' },
3: { id: 3, artist: 'Jahmiel', title: 'Instant Disaster' },
4: { id: 4, artist: "Rappin' 4Tay", title: "Player's Club" },
5: { id: 5, artist: 'JMSN', title: 'Let You Go' },
6: { id: 6, artist: 'Minor Threat', title: "I Don't Wanna Hear It" },
},
playlists: {
1: { id: 1, title: 'Playlist 1', songs: [1, 2, 6, 3, 4, 2] },
2: { id: 2, title: 'Playlist 2', songs: [4, 5, 6, 2] },
},
}
const store = createStore((state = initialState, { type, payload }) => {
switch (type) {
case 'PLAYER/PLAY_SONG':
const playlistID = payload.nextCursor.playlist
const { title: playlistTitle, songs } = state.playlists[playlistID]
const { index } = payload.nextCursor
const { artist, title } = state.songs[songs[index]]
return {
...state,
cursor: payload.nextCursor,
current: `${playlistTitle}: ${artist} - ${title}`,
}
case 'UI/VIEW_PLAYLIST':
if (state.ui.playlist === payload.playlistID) return state
else
return {
...state,
ui: { playlist: payload.playlistID },
}
default:
return state
}
})
const viewPlaylist = playlistID => ({
type: 'UI/VIEW_PLAYLIST',
payload: { playlistID },
})
const playSong = nextCursor => ({
type: 'PLAYER/PLAY_SONG',
payload: { nextCursor },
})
const connectPlaylistList = connect(
({ playlists, ui }) => ({ playlists, ui }),
{ viewPlaylist },
({ playlists, ui }, { viewPlaylist }) => ({
playlists: Object.keys(playlists)
.map(id => playlists[id])
.map(({ id, title }, index) => ({
key: `${index}-${id}`,
title,
setActive: () => viewPlaylist(id),
isActive: id === ui.playlist,
})),
}),
)
const connectSongList = connect(
state => state,
{ playSong },
({ songs, playlists, cursor, ui }, { playSong }) => ({
songs: ui.playlist
? playlists[ui.playlist].songs
.map(id => songs[id])
.map(({ id, artist, title }, index) => ({
key: `${index}-${id}`,
label: `${artist} - ${title}`,
isPlaying:
cursor.playlist === ui.playlist && cursor.index === index,
play: () =>
playSong({
index,
playlist: ui.playlist,
}),
}))
: [],
}),
)
const connectPlayer = connect(({ current }) => ({ current }))
const PlaylistList = connectPlaylistList(({ playlists }) => (
<nav>
{playlists.map(({ key, setActive, title, isActive }) => (
<div key={key}>
{isActive ? (
<strong>{title}</strong>
) : (
<Button handleClick={setActive}>{title}</Button>
)}
</div>
))}
</nav>
))
const SongList = connectSongList(({ songs }) => (
<ul>
{songs.map(({ key, isPlaying, label, play }, i) => (
<li key={key}>
{isPlaying ? (
<strong>{label}</strong>
) : (
<Button handleClick={play}>{label}</Button>
)}
</li>
))}
</ul>
))
const Player = connectPlayer(({ current }) => (
<p>{current || 'Please select a playlist and song!'}</p>
))
function Button({ children, handleClick }) {
return (
<button type="button" className="link-button" onClick={handleClick}>
{children}
</button>
)
}
ReactDOM.render(
<Provider store={store}>
<PlaylistList /><hr />
<Player /><hr />
<SongList />
</Provider>,
document.querySelector('#root'),
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment