Home > other >  confused by state/props and updating UI in nextjs app
confused by state/props and updating UI in nextjs app

Time:08-14

My nextjs app uses getInitialProps to fetch data from a streaming API, and then checks every second thereafter for updates. When the API changes (when the current track ends and a new one begins) I would like for the trackName to change on screen. What I have here works so long as I log the output to console, but for some reason the UI will not update.

I believe that trackName is passed down to the child as a prop? And then the child is trying to set the state in componentDidMount? My understanding of this feels shaky.

This is my _app.js:

import React, { useState } from 'react'
import Player from '../components/Player'

function MyApp({ Component, pageProps, tracks }) {
  const url = "https://kchungradio.out.airtime.pro/kchungradio_a"
  const trackName = tracks.current.name
  return (
    <>
      <Player
        url={url}
        trackName={trackName}
      />
      <Component {...pageProps} />
    </>
  )
}

MyApp.getInitialProps = async (appContext) => {
  let res = await fetch("https://kchungradio.airtime.pro/api/live-info-v2")
  let data = await res.json()

  return { tracks: data.tracks }
}

export default MyApp

And this is my Player:

import { Component } from 'react'
import React from 'react'
import ReactPlayer from 'react-player'

export default class Player extends React.Component {
  constructor(props) {
    super(props)
  }

  componentDidMount() {
    setInterval(() => {
      fetch("https://kchungradio.airtime.pro/api/live-info-v2", {
        method: "GET"
      })
      .then(response => {
        if (!response.ok) {
          return Promise.reject(response)
        }
        return response.json()
      })
      .then(data => {
        this.updateCurrentTrack(data.tracks.current.name)
      })
      .catch(error => {
        if (typeof error.json === "function") {
          error.json().then(jsonError => {
            console.log(jsonError)
          }).catch(genericError => {
            console.log("Generic error from API")
            console.log(error.statusText)
          })
        } else {
          console.log("Fetch error")
          console.log(error)
        }
      })
    }, 1000)
  }

  updateCurrentTrack = (name) => {
    this.setState({ 
      trackName: name
    })
    console.log(name)
  }

  render() {
    const { url, trackName } = this.props
    return (
      <div >
        <ReactPlayer
          url={url}
        />
        <div>{trackName}</div>
      </div>
    )
    
  }
}

CodePudding user response:

this.props.trackName is not the same than this.state.trackName. You are setting a state element that is never declared in the contructor and using the trackName froms this.props which is only received once from it's parent component.

try this in the constructor:

  constructor(props) {
    super(props)

    this.state = {
        trackName: '',
    }

  }

and in your jsx

  render() {
    const { url, trackName } = this.props
    const { trackName: stateTrackName } = this.state // rename since trackName has already been declared
    return (
      <div >
        <ReactPlayer
          url={url}
        />
        <div>{stateTrackName || trackName}</div>
        // or whats the same
        // <div>{this.state.trackName || this.props.trackName}</div>
      </div>
    )
  }

CodePudding user response:

my idea is that please use useState and useEffect. in my next.js applications,

  1. i import useEffect and useState from react.
  2. create 2 constact in useState.
  3. create a useEffect that and in the array of useEffect, set first const.
  4. in the useEffect, change ammount of second const.

this code is a dashboard page of one of my apps.

// IMPORT CTRL
import DashboardCtrl from "../../components/dashboard/ctrl";

// IMPORT DETAILS COMPONENTS
import UserInfo from "../../components/dashboard/userInfo";
import NewProjects from "../../components/dashboard/newProjects";


import { useState,useEffect } from "react";

const Dashboard = () => {

    const [content,setcontent]=useState("UserInfo");
    const [details,setdetails]=useState(<UserInfo/>);

    useEffect(()=>{
        if (content=="UserInfo") {
            setdetails(<UserInfo/>);
        }else if(content=="NewProjects"){
           setdetails(<NewProjects/>);
        }
    },[content])


    return (
        <main>
            <div className=" container mx-auto flex justify-between items-start my-8">
                <div className=" w-1/4">
                    {details}
                </div>
                <div className=" w-1/4">
                    <DashboardCtrl setcontent={setcontent}/>
                </div>
            </div>
        </main>
    );
}

export default Dashboard;

  • Related