Can only update a mounted or mounting component. due to Router - reactjs

I want to write a musicplayer with react.js. After init works ths player well.
But once I change the site with router to music list, the console show me the error:
Can only update a mounted or mounting component. This usually means
you called setState() on an unmounted component. This is a no-op.
Please check the code for the Player component
I have marked the line in code.
I think, while I am changing the site, the player component may be unmounted. But wihtout the componentWillUnmount function, nothing different.
Here is my project.
Thanks in advance!
player.js
import React, {Component} from 'react';
import '../../styles/player.less';
import Progress from '../commen/progress.js';
import {Link} from 'react-router-dom';
let duration = null;
export default class Player extends Component {
constructor(props){
super(props);
this.state={
progress: 0,
volume: 0,
isPlay: true,
},
this.play=this.play.bind(this);
this.progressChangeHandler=this.progressChangeHandler.bind(this);
this.volumeChangeHandler=this.volumeChangeHandler.bind(this);
}
componentDidMount(){
$('#player').bind($.jPlayer.event.timeupdate,(e)=>{
duration = e.jPlayer.status.duration;//total duration of the song
this.setState({//here is the problem !!!!!!!!!!!!!!!!!!!!!!!
volume: e.jPlayer.options.volume*100,
progress:e.jPlayer.status.currentPercentAbsolute
});//how lange already played
});
}
componentWillUnMount(){
//$('#player').unbind($.jPlayer.event.timeupdate);
}
//从子组件中获得值
//change progress
progressChangeHandler(progress){
$('#player').jPlayer('play', duration * progress);
}
//change volume
volumeChangeHandler(progress){
$('#player').jPlayer('volume', progress);
}
//play pause switcher
play(){
if(this.state.isPlay){
$('#player').jPlayer('pause');
}else{
$('#player').jPlayer('play');
}
this.setState({
isPlay: !this.state.isPlay,
})
}
render(){
return(
<div className="player-page">
<h1 className="caption">
<Link to="/list">My Favorite Music</Link>
</h1>
<div className="mt20 row">
<div className = "controll-wrapper">
<h2 className="music-title">{this.props.currentMusicItem.title}</h2>
<h3 className="music-artist mt10">{this.props.currentMusicItem.artist}</h3>
<div className="row mt20">
<div className="left-time -col-auto">-2:00</div>
<div className="volume-container">
<i className="icon-volume rt"></i>
<div className="volume-wrapper">
<Progress
progress={this.state.volume}
onProgressChange={this.volumeChangeHandler}
barColor="#aaa"></Progress>
</div>
</div>
</div>
<div className="progress-container">
<Progress
progress={this.state.progress}
onProgressChange={this.progressChangeHandler}>
</Progress>
</div>
<div className="mt35 row">
<div>
<i className="icon prev"></i>
<i className={`icon ml20 ${this.state.isPlay ? 'pause' : 'play'}`} onClick={this.play}></i>
<i className="icon next ml20"></i>
</div>
<div className="-col-auto">
<i className="icon repeat-cycle"></i>
</div>
</div>
</div>
<div className="-col-auto cover">
<img src={this.props.currentMusicItem.cover} alt={this.props.currentMusicItem.title}/>
</div>
</div>
</div>
/**<div className="player-page">
<Progress progress={this.state.progress} onProgressChange={this.progressChangeHandler}></Progress>
</div>**/
)
}
}
root.js
import React, {Component} from 'react';
import Header from './commen/header.js';
import Player from './page/player.js';
import {MUSIC_LIST} from '../config/musiclist';
import MusicListUI from './page/musiclistui.js';
import {BrowserRouter as Router, Switch, Route, Link} from 'react-router-dom';
export default class Root extends Component{
constructor(props){
super(props);
this.state={
musiclist: MUSIC_LIST,
currentMusicItem: MUSIC_LIST[0]
}
}
componentDidMount(){
$('#player').jPlayer({
ready:function(){
$(this).jPlayer('setMedia',{
mp3:'http://oj4t8z2d5.bkt.clouddn.com/%E9%AD%94%E9%AC%BC%E4%B8%AD%E7%9A%84%E5%A4%A9%E4%BD%BF.mp3'
}).jPlayer('play');
},
supplied:'mp3',
wmode: 'window'
});
}
render(){
const Home=() => (
<Player
currentMusicItem={this.state.currentMusicItem}
/>
);
const List = () => (
<MusicListUI
currentMusicItem={this.state.currentMusicItem}
musiclist={this.state.musiclist}
/>
);
return(
<Router>
<div>
<Header/>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/list" component={List}/>
</Switch>
</div>
</Router>
)
}
}

Error sums it up well. When you change the route component gets unmounted and callback tries to setState on unmounted component. You have to figure out how to do clean up before this happens. I see you already figured out that you can use componentWillUnmount, but you have a typo:
componentWillUnMount(){ // should be componentWillUnmount!
//$('#player').unbind($.jPlayer.event.timeupdate);
}
Notice also that bind method has been deprecated.

Router is not your issue. Your issue is that you are fighting a fundamental design paradigm of React... or perhaps common sense. You are not supposed to try and perform operations on components that don't currently don't exist. What would that accomplish? In your case, you have a bunch of event listeners that you haven't removed.
As a side note, you are using an incredible amount of jQuery. You should be using this.setState as often as you can. You're programming in a style that goes against some of the best aspects of React.

Related

looping json data in Class Component

This is my index.js where I try to refer SampleApp
import React, { Component } from "react";
import { render } from "react-dom";
import './index.css';
import "../node_modules/bootstrap/dist/css/bootstrap.min.css";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
Redirect
} from "react-router-dom";
import SampleApp from "./pages/SampleApp";
import 'bootstrap/dist/css/bootstrap.min.css';
class App extends Component {
constructor() {
super();
this.state = {
name: "React",
isUserAuthenticated: true
};
}
render() {
return (
<Router>
<Switch>
<Route
exact
path="/"
render={() => {
return (
<Redirect to="/SampleApp" />
)
}}
/>
<Route exact path="/SampleApp" component={SampleApp} />
</Switch>
</Router>
);
}
}
render(<App />, document.getElementById("root"));
This is my SampleApp.js file. here I'm importing the Cards component from Cards.jsx
import React from 'react';
import '../../src/App.css';
import Cards from '../cards/cards';
const SampleApp = props => (
<React.Fragment>
<div className="App">
<div>
<div className="header">
<div className="header_fonts">
Sample Application
</div>
</div>
<div>
<div className="content_header_fonts">
This is sample app
</div>
<div className="content_fonts">
Sample app to deomntrate ideas.
</div>
<Cards></Cards>
</div>
</div>
</div>
</React.Fragment>
)
export default SampleApp;
this is my Cards.jsx file.
here I'm importing Card component and json data
import React, { Component } from "react";
import Card from './cardUI';
import CardData from '../source/data.json';
class Cards extends Component {
render() {
return
(
<div className="container-fluid d-flex justify-content-center">
<div className="row">
{
CardData.map((
{title, desc, icon, intro,developer_guide,api_ref }, id) =>
(
<div className="col-md-4">
<Card
title={title}
desc={desc}
intro={intro}
developer_guide={developer_guide}
api_ref={api_ref}/>
</div>
))
}
</div>
</div>
);
}
}
export default Cards;
this is a sample of my JSON file
[
{
"id" : 7,
"title" : "Melon Munchee",
"icon" : "https://cdn.onlinewebfonts.com/svg/img_393496.png",
"desc" : "If you are an Avatar fan, then this api is for you. Here you can find everything from Episodes to Characters.",
"intro": "intro_7",
"developer_guide": "d_link7",
"api_ref": "api_link7"
},
{
"id" : 8,
"title" : "Browns Barns",
"icon" : "https://cdn.onlinewebfonts.com/svg/img_386567.png",
"desc" : "Baseball fans? Computer nerds? Now, in one place, you have baseball data and an api to access it. Have fun!.",
"intro": "intro_8",
"developer_guide": "d_link8",
"api_ref": "api_link8"
}
]
Card.jsx file
This is how implemented the Card component
import React from 'react';
import "../../node_modules/bootstrap/dist/css/bootstrap.min.css";
import * as Icon from '../../node_modules/react-bootstrap-icons';
import './card-style.css';
const Card = props =>{
return(
<div className="card text-center">
<div className="card-body text-dark">
<Icon.Alarm></Icon.Alarm>
<h4 className="card-title">
{props.title}
</h4>
<p className="card-text text-secondary">
{props.desc}
</p>
<ul class="list-group">
<li class="list-group-item">{props.intro}</li>
<li class="list-group-item">{props.developer_guide}</li>
<li class="list-group-item">{props.api_ref}</li>
</ul>
</div>
</div>
)
}
export default Card;
but I'm getting an error as following
Error: Cards(...): Nothing was returned from render. This usually
means a return statement is missing. Or, to render nothing, return null.
19 stack frames were collapsed. Module.
src/index.js:44 41 | } 42 | } 43 |
44 | render(, document.getElementById("root"));
I am actually going to take a guess here and say that your specific error is caused by the new line after your return statement. So remove it to make it look like this return ( and it should work... or at least that error should go away.
Check out this sandbox: https://codesandbox.io/s/xenodochial-fog-y8pk2?file=/src/App.js
just go ahead and add a new line after the return and see your exact error.
It's just a typo mistake. When you use line terminator next to the return statement, JS adds semicolon automatically and that will be the end of function execution and returns undefined. That's why your Cards component is not able to find the JSX because Cards render returns undefined.
As per MDN docs.
The return statement is affected by automatic semicolon insertion (ASI). No line terminator is allowed between the return keyword and the expression.
To fix this, update Cards render function with this
class Cards extends Component {
render() {
return ( // was the issue earlier
<div className="container-fluid d-flex justify-content-center">
<div className="row">
{
CardData.map(({ title, desc, icon, intro,developer_guide,api_ref }, index) => (
<div className="col-md-4" key={title + index}>
<Card
title={title}
desc={desc}
intro={intro}
developer_guide={developer_guide}
api_ref={api_ref} />
</div>
)
)
}
</div>
</div>
);
}
}

React Link doesn't refresh page automatically

I am currently experiencing an issue similar to React Link doesn't refresh the page, however, the answer doesn't work well for my case.
See, I am currently using react-router to have a path called 'study/:id'. This :id variable will just be printed on the page
Here is the code for my BrowserRouter (App.js)
import React from 'react';
import './App.css';
import HomePage from './HomePage/HomePage';
import Study from './StudyPage/Study';
import {BrowserRouter as Router, Route, Switch } from 'react-router-dom';
function App() {
return (
<Router>
<Switch>
<Route path="/" exact={true} component={HomePage}/>
<Route path="/Study/:id" exact={true} component={Study} />
</Switch>
</Router>
);
}
export default App;
Inside the Study component itself, it basically just has a menubar and an indicator on which courseId are we in:
import React from 'react';
import './Study.css';
import Menubar from '../Menubar';
import Sidebar from './Sidebar';
import Chapter from './Chapter';
class Study extends React.Component{
constructor(props){
super(props);
this.state = {
courseId: this.props.match.params.id,
};
}
render(){
return(
<div id="studyWrapper">
<Menubar />
<h1>We are on course: {this.state.courseId}</h1>
</div>
)
}
}
export default Study;
In order for the user to navigate through the study pages, I use a menubar component like this (Menubar.js)
import React from 'react';
import './Menubar.css';
import { Nav } from 'reactstrap';
import { Dropdown, DropdownButton } from 'react-bootstrap';
import { Link } from 'react-router-dom';
class Menubar extends React.Component{
constructor(props){
super();
this.state = {
courses: [],
reload: false
}
}
async componentDidMount(){
const response = await fetch("/v1/courses/")
const body = await response.json();
this.setState({
courses: body
});
}
render(){
const {courses} = this.state
return (
<Nav className="navbar navbar-expand-md navbar-light menubarStyle fixed-top">
<div className="container-fluid">
<a className="navbar-brand logo" href="/">LOGO</a>
<div className="navbar-collapse">
<div className="navbar-nav">
<div className="dropdown nav-item">
<DropdownButton variant='Secondary' id="dropdown-basic-button" title="Browse Courses">
<Dropdown.Item as={Link} to={`/study/001`} >001</Dropdown.Item>
<Dropdown.Item as={Link} to={`/study/002`} >002</Dropdown.Item>
<Dropdown.Item as={Link} to={`/study/003`} >003</Dropdown.Item>
</DropdownButton>
</div>
</div>
</div>
</div>
</Nav>
)
}
}
export default Menubar
IRL, the study page basically looks like this
The problem
The problem that I am having is that, once I am in '/study/001' page already (just like the picture above). If I try to click on DropdownItem 002 from the menuBar, the URL will change to 'study/002', but the page won't change. It will not refresh.
The solution from React Link doesn't refresh the page basically says to use windows.location.reload() but that doesn't work in my case, if we do that, when I click on dropdownItem 002, the URL will change to 'study/002' for a moment, but then 'study/001' will refresh thus making the page back to 001
My question is, is there a way for us to refresh the page whenever the url is changed by link ?
Or if not, are there any other methods that I can use for this design? Maybe using links is not the right way in the first place?
Pardon the long post, I try to make it as clear as possible.
Thank you !
Inside your Study component you could use componentDidUpdate and compare the current props with the prevProps to check if the url has changed and then change the state, which should cause your component to update. More or less you would have this code:
componentDidUpdate(prevProps) {
if( this.props.match.params.id !== prevProps.match.params.id ){
this.setState({ courseId: this.props.match.params.id })
};
}

No Set State or Async but still "Can't perform state update on unmounted component" error

I have nearly given up with this. I can't get my component to work no matter what I do. I have read all the error posts here and on Google but still nothing. I get the error
Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
Here is my full code...
import React, { Component } from 'react;
// page title bar
import PageTitleBar from 'Components/PageTitleBar/PageTitleBar';
import {
NewsFeedWidget
} from "Components/Widgets";
// widgets data
import {
newsData,
} from './data';
export default class EcommerceDashboard extends Component {
render() {
return (
<div className="ecom-dashboard-wrapper">
<div className="content_container">
<div className="row">
<div className="col-sm-6 col-md-4 w-xs-full">
<NewsFeedWidget data={newsData} />
</div>
</div>
</div>
</div>
)
}
}
and NewsFeed.js
import React, { Component, Fragment } from 'react';
const NewsFeedWidget = ({ data }) => (
<div className="cardheader_container">
<div className="notification_container">
{data && data.map((d, key) => (
<div className="a_notification" key={key}>
<div className="not_header">
<div className="not_content">
<h5>{d.title}</h5>
<p>{d.subtitle}</p>
</div>
</div>
</div>
))}
</div>
</div>
}
export default NewsFeedWidget;
Obviously I am new at React so please explain how and what issue I am having. It's gonna be something dumb I bet but this is how i'll learn.
ty
Could you upload the code for PageTitleBar too ?

Using fullpagejs in React, how to trigger function on active slide without re-rendering entire page

In my React app I am using fullpage.js to render two slides containing two different components. I want to run a function inside one of these only when it's the active slide. I tried below code, but once the state changes the entire ReactFullpage is re-rendered causing the first slide to be active again so I'm basically stuck in a loop.
My question is, how can I trigger a function inside the <Player /> component to run only if it's the active slide?
import React from "react";
import ReactFullpage from "#fullpage/react-fullpage";
import AlbumInfo from './AlbumInfo';
import Player from './Player';
class Album extends React.Component {
constructor(props){
super(props);
this.state={
playing: false
}
}
_initPlayer = (currentIndex, nextIndex) => {
if(nextIndex.index === 1) {
this.setState({playing:true})
}
}
render() {
return (
<ReactFullpage
licenseKey='xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx'
sectionsColor={["#000000"]}
afterLoad={this._initPlayer}
render={({ state, fullpageApi }) => {
return (
<div id="fullpage-wrapper">
<div className="section">
<AlbumInfo />
</div>
<div className="section">
<Player playing={this.state.playing} />
</div>
</div>
);
}}
/>
);
}
}
export default Album;
From docs:
just add the class 'active' to the section and slide you want to load first.
adding conditionally (f.e. using getActiveSection()) 'active' class name should resolve rerendering problem.
The same method/value can be used for setting playing prop.
Probably (I don't know/didn't used fullpage.js) you can also use callbacks (without state management and unnecessary render), f.e. afterSlideLoad
Update
The issue has been fixed on https://github.com/alvarotrigo/react-fullpage/issues/118.
Version 0.1.15 will have it fixed
You should be using fullPage.js callbacks afterLoad or onLeave as can be seen in the codesandbox provided on the react-fullpage docs:
https://codesandbox.io/s/m34yq5q0qx
/* eslint-disable import/no-extraneous-dependencies */
import React from "react";
import ReactDOM from "react-dom";
import "fullpage.js/vendors/scrolloverflow"; // Optional. When using scrollOverflow:true
import ReactFullpage from "#fullpage/react-fullpage";
import "./styles.css";
class FullpageWrapper extends React.Component {
onLeave(origin, destination, direction) {
console.log("Leaving section " + origin.index);
}
afterLoad(origin, destination, direction) {
console.log("After load: " + destination.index);
}
render() {
return (
<ReactFullpage
anchors={["firstPage", "secondPage", "thirdPage"]}
sectionsColor={["#282c34", "#ff5f45", "#0798ec"]}
scrollOverflow={true}
onLeave={this.onLeave.bind(this)}
afterLoad={this.afterLoad.bind(this)}
render={({ state, fullpageApi }) => {
return (
<div id="fullpage-wrapper">
<div className="section section1">
<h3>Section 1</h3>
<button onClick={() => fullpageApi.moveSectionDown()}>
Move down
</button>
</div>
<div className="section">
<div className="slide">
<h3>Slide 2.1</h3>
</div>
<div className="slide">
<h3>Slide 2.2</h3>
</div>
<div className="slide">
<h3>Slide 2.3</h3>
</div>
</div>
<div className="section">
<h3>Section 3</h3>
</div>
</div>
);
}}
/>
);
}
}
ReactDOM.render(<FullpageWrapper />, document.getElementById("react-root"));
export default FullpageWrapper;

how to meteor react subscribe data bind in component?

I am setting up a new meteor react app which subscribes new data from the server. I want to only bind data to main page component. I have following code main page componen.
import React from 'react';
import ReactDOM from 'react-dom';
import RealTime from '../../../lib/client/RealTime';
// TrackerReact is imported (default) with Meteor 1.3 new module system
import TrackerReact from 'meteor/ultimatejs:tracker-react';
import Game from '../pages/components/game';
// > React.Component is simply wrapped with TrackerReact
class MainPage extends TrackerReact(React.Component) {
// Note: In ES6, constructor() === componentWillMount() in React ES5
constructor() {
super();
this.state = {
subscription: {
tasks: Meteor.subscribe('userData')
},
data:{}
}
}
getingData(){
let data=RealTime.find().fetch();
return data;
}
render() {
const gamedata=this.getingData();
console.log(this.getingData());
return (
<div className="container ">
<div className="board-player-top">
<img className="user-pic" src="../../../../../images/player-img-top.png" alt="" title=""/>
<div className="board-player-userTagline">
<div className="user-tagline-component">
Black Name
<i>2202<img src="../../../../../images/user-flag.png" alt=""/></i>
</div>
<div className="captured-pieces">
<img src="images/small-picW-1.png" /> <img src="images/small-picW-2.png" />
</div>
<div className="clock-top">
10:00
</div>
</div>
</div>
<Game/>
<div className="board-player-bottom">
<img className="user-pic" src="../../../images/player-img-bottom.png" alt="" title=""/>
<div className="board-player-userTagline">
<div className="user-tagline-component">
Staick <i>1576<img src="../../../images/user-flag.png" alt=""/></i>
</div>
<div className="captured-pieces">
<img src="images/small-picB-1.png" /> <img src="images/small-picB-2.png" />
</div>
<div className="clock-bottom active">
10:00
</div>
</div>
</div>
</div>
)
}
};
export default MainPage
i have console.log(this.getingData()) inside render found
following data from serverside which automatically update.
Application comminucation with help of socket io other server and data publish to meteor client.application layout is following structer
You can pass data to child component using props but here you can find problem each time you have received the gamedata pass as props to child component then child component will update.
You should simantanilious pass data to each child component.

Resources