2

My api data is being successfully passed from my api call into the table component but is not being rendered.

If after searching for a playlist I go in and make an edit to the table.js file the data will render correctly. App.js...

const App = (props) => {
  const [playlists, setPlaylists] = useState([])
  const [searchString, setSearchString] = useState() //use instead of onsubmit 
  const isFirstRef = useRef(true);
  
  const search = (value) => {
    setSearchString(value)
  }

  useEffect(
    () => {
      if (isFirstRef.current) {
      isFirstRef.current = false;
      return;
      }
      let spotlist = Spotify.playlistsearch(searchString)
      let tablelist = []
      spotlist.then(val =>{
        val.forEach(element =>{
          tablelist.push(
            { 
              name: element.description,
              track_count: element.tracks.total,
            })
        } 
      )})
      setPlaylists(tablelist)
      }, [searchString] );

  return (
    <div className="App">
      <Searchform search={search}/>
      <Table playlists={playlists}/>
    </div>
  )
};

The playlists prop is being shown as present under the PlayListTable component inspector but is not rendering. I am able to get the data to render IF I edit the file after seeing the data present in the component inspector. Table.js

import React from 'react'
import { Icon, Label, Menu, Table } from 'semantic-ui-react'

const PlayListTable = ({ playlists }) => {
  return(
    <Table celled>
      <Table.Header>
        <Table.Row>
          <Table.HeaderCell>Playlist</Table.HeaderCell>
          <Table.HeaderCell>Track Count</Table.HeaderCell>
        </Table.Row>
      </Table.Header>
      <Table.Body>
       {playlists.map( (playlist) => {
          return (
              <Table.Row>
                <Table.Cell>
                  {playlist.name}
                </Table.Cell>
                <Table.Cell>
                  {playlist.track_count}
                </Table.Cell>
              </Table.Row>
              )
            }
          )
        }
      </Table.Body>
      <Table.Footer>
        <Table.Row>
          <Table.HeaderCell colSpan='3'>
            <Menu floated='right' pagination>
              <Menu.Item as='a' icon>
                <Icon name='chevron left' />
              </Menu.Item>
              <Menu.Item as='a'>1</Menu.Item>
              <Menu.Item as='a'>2</Menu.Item>
              <Menu.Item as='a'>3</Menu.Item>
              <Menu.Item as='a'>4</Menu.Item>
              <Menu.Item as='a' icon>
                <Icon name='chevron right' />
              </Menu.Item>
            </Menu>
          </Table.HeaderCell>
        </Table.Row>
      </Table.Footer>
    </Table>
  )
}

export default PlayListTable
Casey
  • 23
  • 3

2 Answers2

1

Issue

You may be receiving correct data but you don't update your state at the correct time. spotlist returns a Promise that you are chaining from, but the setPlaylists(tablelist) call is enqueued before the then block of the promise chain is processed. The useEffect callback is 100% synchronous code.

let spotlist = Spotify.playlistsearch(searchString);
let tablelist = [];
spotlist.then(val => { // <-- (1) then callback is placed in the event queue
  val.forEach(element => { // <-- (3) callback is processed, tablelist updated
    tablelist.push({ 
      name: element.description,
      track_count: element.tracks.total,
    });
  } 
)});
setPlaylists(tablelist); // <-- (2) state update enqueued, tablelist = []

Solution - Place state update in Promise chain

You can forEach into a temp array, but mapping the response data to state values is the more "React" way of handling it. It is also more succinct.

Spotify.playlistsearch(searchString)
  .then(val => {
    setPlaylists(val.map(element => ({
      name: element.description,
      track_count: element.tracks.total,
    })));
  } 
)});
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
0

The first note I’d have is that your use effect will not trigger unless something changes with searchString since it’s in the dependency array. Additionally, I believe that your first ref condition prevents the first render from setting up, so it could be causing issues.

It may be better to look into something like a previous check, I’ve used the solution for a common hook from here before: How to compare oldValues and newValues on React Hooks useEffect?

When you say the playlist prop is showing up, are there actual values?

On other edit make sure the return value from map has proper keys: https://reactjs.org/docs/lists-and-keys.html

Grace Tay
  • 1
  • 3