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,
- i import useEffect and useState from react.
- create 2 constact in useState.
- create a useEffect that and in the array of useEffect, set first const.
- 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;