Currently working on a personal project and i'm getting this error
Warning: Can only update a mounted or mounting component. This usually means you called setState, replaceState, or forceUpdate on an unmounted component. This is a no-op. Please check the code for the Graph component.
I also get the same issue for my App component. I've done some reading on what the issue could be, but i'm non the wiser as to what the issue is with my code specifically.
Any insight would be greatly appreciated.
Here is a link to the running project (with sourcecode) on CodeSnadbox.io I've linked the offending code below as well.
Here is the Graph Component
import React, { Component } from "react";
import { render } from "react-dom";
import { Line, Doughnut, Bar } from "react-chartjs-2";
import moment from "moment";
import PropTypes from "prop-types";
import styleConstants from "../misc/style_constants.js";
class Graph extends Component {
constructor(props) {
super(props);
this.state = {
label: "default",
dataset: [],
labels: []
};
}
/**
* https://min-api.cryptocompare.com/ for documentation
*/
async getHistoryData(ticker = "BTC", currency = "USD", filter = "close") {
try {
let response = await fetch(
`https://min-api.cryptocompare.com/data/histoday?fsym=${ticker}&tsym=${currency}&limit=60&aggregate=3&e=CCCAGG`
);
const responseJson = await response.json();
const dataset = responseJson.Data.map(data => {
return data[filter];
});
const labels = responseJson.Data.map(data => {
return moment(new Date(data.time * 1000)).format("MMM Do YY");
});
this.setState({ dataset: dataset });
this.setState({ labels: labels });
} catch (error) {
console.log(error);
}
}
componentDidMount() {
const { ticker, currency, filter } = this.props;
this.getHistoryData(ticker, currency, filter);
}
render() {
const { label, graphType } = this.props;
const { dataset, labels } = this.state;
const options = {
legend: {
fontColor: styleConstants.get("Dark")
},
scales: {
yAxes: [
{
ticks: {
fontColor: styleConstants.get("Light"),
beginAtZero: true,
callback: function(value, index, values) {
if (parseInt(value) >= 1000) {
return (
"$" + value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
);
} else {
return "$" + value;
}
}
}
}
],
xAxes: [
{
ticks: {
fontColor: styleConstants.get("Light"),
fontSize: 10,
stepSize: 1,
beginAtZero: true
}
}
]
}
};
const data = {
labels: labels,
datasets: [
{
label: label,
fill: true,
lineTension: 0.1,
backgroundColor: styleConstants.get("Medium"),
borderColor: styleConstants.get("Medium"),
borderCapStyle: "butt",
borderDash: [],
borderDashOffset: 0.0,
borderJoinStyle: "miter",
pointBorderColor: styleConstants.get("Light"),
pointBackgroundColor: "#fff",
pointBorderWidth: 1,
pointHoverRadius: 5,
pointHoverBackgroundColor: "rgba(75,192,192,1)",
pointHoverBorderColor: "rgba(220,220,220,1)",
pointHoverBorderWidth: 2,
pointRadius: 1,
pointHitRadius: 10,
data: dataset
}
]
};
return <Line data={data} options={options} />;
// switch (graphType) {
// case "line":
// return <Line data={data} options={options} />;
// break;
// case "bar":
// return <Bar data={data} options={options} />;
// break;
// case "doughnut":
// return <Doughnut data={data} options={options} />;
// break;
// default:
// return null;
// }
}
}
Graph.propTypes = {
label: PropTypes.string,
graphType: PropTypes.string
};
Graph.defaultProps = {
label: "Default String",
graphType: "Default String"
};
export default Graph;
Here is the App Component also
import React, { Component } from "react";
import { render } from "react-dom";
import styled, { css } from "styled-components";
import styleConstants from "../misc/style_constants.js";
import Overview from "../components/Overview";
import Panel from "../components/Panel";
import Table from "../components/Table";
import Options from "./Options";
import Graph from "./Graph";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
selectedTicker: "BTC",
currency: "USD",
tickers: [],
overview: []
};
this.updateTicker = this.updateTicker.bind(this);
this.createGraph = this.createGraph.bind(this);
}
updateTicker(selectedValue) {
const { value } = selectedValue;
this.setState({ selectedTicker: value });
}
async getTickers() {
try {
const response = await fetch('https://api.coinmarketcap.com/v1/ticker/')
const responseJSON = await response.json();
this.setState({ tickers: responseJSON });
} catch (error) {
console.log("App getTickers() ", error);
}
}
async getOverviewData() {
try {
const response = await fetch(`https://api.coinmarketcap.com/v1/global/?convert=${this.state.currency}`)
const responseJSON = await response.json();
this.setState({ overview: responseJSON });
} catch (error) {
console.log("App getOverviewData() ", error);
}
}
componentDidMount() {
this.getTickers();
this.getOverviewData();
}
createGraph(ticker = "", currency = "", graphType = "", label = "", filter = "") {
return (
<Graph
filter={filter}
ticker={ticker}
currency={currency}
graphType={graphType}
label={label}
/>
)
}
render() {
const { selectedTicker, currency } = this.state;
const Container = styled.div`
input:focus,
select:focus,
textarea:focus,
`;
const Title = styled.h1`
text-align: center;
color: ${styleConstants.get('Yellow')};
`;
const LightSpan = styled.span`
font-weight: 200;
`;
return (
<Container>
<Title>
Coin:<LightSpan>Dash</LightSpan>
</Title>
<Overview {...this.state.overview} />
<Options
selectedValue={this.state.selectedTicker}
values={this.state.tickers.map(data => {
return data.symbol;
})}
labels={
this.state.tickers.map(data => {
return data.id;
})
}
updateTicker={this.updateTicker} />
<Panel label={"Price Action"} content={this.createGraph(selectedTicker, currency, 'line', "Close", "close")} />
<Panel label={"Highest Price"} content={this.createGraph(selectedTicker, currency, 'bar', "High", "high")} />
<Panel label={"Lowest Price"} content={this.createGraph(selectedTicker, currency, 'bar', "Low", "low")} />
<Panel label={"Top Ten List"} content={
<Table header={["Rank", "Name", "Price", "Change(24 Hour)"]} collection={this.state.tickers} />
} />
</Container>
);
}
}
The problem is caused by stateless functional components that are defined inside the render method of App. If you define the following functions outside of the App class, the error is fixed:
const Container = styled.div`
input:focus,
select:focus,
textarea:focus,
`;
const Title = styled.h1`
text-align: center;
color: ${styleConstants.get('Yellow')};
`;
const LightSpan = styled.span`
font-weight: 200;
`;
export default class App extends Component {
..
The reason for the failure is that locally created SFC's change on each render, which causes them to unmount and remount, even though the rendering stays the same. There are also some other local SFC's in the Table component, which did not create any warning, but do cause unnecessary remounting.
UPDATE: It was a bit of a puzzle, but the remaining warning came from one of the tests:
describe("App", () => {
it("renders without crashing", () => {
const div = document.createElement("div");
ReactDOM.render(<App />, div);
ReactDOM.unmountComponentAtNode(div);
});
});
Which makes sense, as you unmount the component before the async action is completed.
Here's a working sandbox: https://codesandbox.io/s/24o6vp4rzp (I've also removed the arrow function in content={..}, since that needs to be a value)
Related
I have this component Chart.js, I've managed to get the value out from console logging the Select component, which is the value in siteOptions variable, my question is how to pass the onchange value to mapStatetoProps below so the key siteSelect is filled with the value of my selected value the Select component? so I can pass the siteSelect as props to ChartLine component
const Charts = ({siteSelect}) => {
const siteOptions = [
{ value: "USTP", label: "USTP" },
{ value: "SMG", label: "SMG" },
{ value: "GCM", label: "GCM" },
{ value: "SJE", label: "SJE" },
{ value: "SBE", label: "SBE" },
{ value: "SLM", label: "SLM" },
];
const [site, setSite] = useState(siteOptions["0"].value);
const handleSiteChange = (e, site) =>{
setSite(site.value)
console.log(site.value)
}
return (
<>
<Grid.Column>
<Select
placeholder="Select Site"
options={siteOptions}
onChange{handleSiteChange}/>
</Grid.Column>
<Grid.Column width={4}>
<ChartLine site={siteSelected} />
</Grid.Column>
</>
const mapStateToProps = (state) => {
return {
siteSelect: ??????
};
};
)
export default connect (mapStateToProps) (Charts);
In Chartline component, when it received the site props value, it will fire up useEffect to dispatch a new parameter and call a new api.
This is my Chartline component
import { connect, useDispatch } from "react-redux";
import { fetchData } from "./Action";
import { useEffect } from "react";
import format from "dateformat"
import _ from "lodash";
import LoadingStatus from "../../../../templates/LoadingStatus";
import LineChart from "../../../../templates/LineChart";
const Charts = ({ chart_10, site, p_date, title = "Line Chart" }) => {
const dispatch = useDispatch();
const options = {
maintainAspectRatio: false,
responsive: true,
};
const labels = chart_10?.map((data) => data["BULAN"]);
const data = {
labels,
datasets: [
{
label: "Actual",
data: chart_10?.map((data) => data["ACTUAL"]),
borderColor: "rgb(255, 99, 132)",
backgroundColor: "rgba(255, 99, 132, 0.5)",
},
{
label: "Budget",
data: chart_10?.map((data) => data["BUDGET"]),
borderColor: "rgb(51, 131, 255)",
backgroundColor: "rgba(51, 131, 255, 0.5)",
},
],
options,
}
useEffect(() => {
dispatch(fetchData(site, p_date));
}, [dispatch, site, p_date]);
if (_.isNull(chart_10)) return <LoadingStatus />;
return (
<LineChart data={data} title={title} />
);
};
const mapStateToProps = (state) => {
return {
chart_10: state.dashboard.chart_10,
p_date: format((state.auth.menu.user.currentdate), "dd-mm-yyyy")
};
};
export default connect(mapStateToProps, { fetchData })(Charts);
I am getting error again and again . I don't know why I am getting this error.
The response I am getting is also an array, I tried with console.log . Below is the proof
Response From the axios Api:
{
"CCompany": "Testing Company",
"CFName": "Rehan",
"CLName": "ahmed",
"CMName": "",
"CTelHome": "1-232-2323232",
"UID": "700002"
}
Below is the code:
import React, { Component } from 'react';
import { View, Text, Dimensions, BackHandler, ToastAndroid } from 'react-native';
import axios from 'axios';
import Card from './Card';
import CardSection from './CardSection';
import ProfileDetails from './ProfileDetails';
import AsyncStorage from '#react-native-community/async-storage';
// Create a component
class ProfileActivity extends Component {
constructor() {
super();
this.state = {
profile: [],
setUID: '',
isloading: true,
};
}
state = {
canBeClosed: false
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton);
}
handleBackButton = () => {
if (this.props.navigation.isFocused()) {
if (this.state.canBeClosed)
return this.state.canBeClosed = false;
else {
setTimeout(() => { this.state.canBeClosed = false }, 3000);
ToastAndroid.show("Press Again To Exit !", ToastAndroid.LONG);
return this.state.canBeClosed = true
}
}
};
async componentDidMount() {
try {
if (this.state.setUID == null && this.state.profile == null) {
console.log('profile remove');
const user = await AsyncStorage.getItem('responseJson');
const parsed = JSON.parse(user);
if (parsed !== null) {
this.setState({ setUID: parsed.UID });
}
axios.get('https:/new.didx.net/didxapi/UserInfo.php?UID=' + this.state.setUID)
.then(response => this.setState({ profile: response.data }));
}
else {
this.setState({ setUID: "" });
this.setState({ profile: "" });
console.log('not remove');
const user = await AsyncStorage.getItem('responseJson');
const parsed = JSON.parse(user);
if (parsed !== null) {
this.setState({ setUID: parsed.UID });
}
axios.get('https:/new.didx.net/didxapi/UserInfo.php?UID=' + this.state.setUID)
.then(response => this.setState({ profile: response.data }));
}
}
catch (error) {
alert('Server Error!')
}
BackHandler.addEventListener('hardwareBackPress', this.handleBackButton);
}
renderProfile() {
if (this.state.profile) {
console.log(this.state.profile);
return this.state.profile.map(profile => (
<ProfileDetails key={profile.UID} profile={profile} />
));
}
}
render() {
return (
<View style={styles.container}>
{this.renderProfile()}
</View>
);
}
}
export default ProfileActivity;
const h = Dimensions.get('screen').height * 0.01;
const w = Dimensions.get('screen').width * 0.01;
const styles = {
container: {
flex: 1,
backgroundColor: '#fff'
},
ViewStyle: {
paddingTop: h * 5,
},
TextStyle: {
justifyContent: 'flex-start',
// alignSelf: 'center',
color: '#000',
fontWeight: 'bold',
fontSize: 20,
padding: 5,
fontFamily: 'Roboto',
maxWidth: w * 50,
}
}
I tried everything I could to solve this problem.
.map expects an array ... but your axios.get('https:/new.didx.net/didxapi/UserInfo.php?UID=' + this.state.setUID) call returns an object like { UID: "1", CFName: "1", CMName: "", CLName: "1", CCompany: "1", CTelHome: "2" }
I am pretty new to React and I am trying to build this simple web app that takes a stock tag as an input and updates the graph based on the performance of the given stock. However, I can't get my graph to update. I tried using componentDidUpdate(prevProps, prevState, snapshot), but for some reason prevProps is undefined and I don't know/understand why. I tried searching online and reading the doc file, but I still can't figure it out. Any help would be appreciated.
import Search from './Search.js'
import Graph from './Graph.js'
import Sidebar from './Sidebar.js'
import './App.css'
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
data: [{
x: [],
close: [],
decreasing: { line: { color: '#FF0000' } },
high: [],
increasing: { line: { color: '#7CFC00' } },
line: { color: 'rgba(31,119,180,1)' },
low: [],
open: [],
type: 'candlestick',
xaxis: 'x',
yaxis: 'y'
}]
,
layout: {
width: 1500,
height: 700,
font: { color: '#fff' },
title: { text: 'Stock', xanchor: "left", x: 0 }, paper_bgcolor: '#243b55', plot_bgcolor: '#243b55', yaxis: { showgrid: true, color: '#fff' },
xaxis: {
zeroline: true, color: '#fff', showgrid: true, rangeslider: {
visible: false
}
}
},
searchfield: '',
stocktag: ' '
};
this.onSearchChange = this.onSearchChange.bind(this);
this.onSubmitSearch = this.onSubmitSearch.bind(this);
}
componentDidMount() {
document.body.style.backgroundColor = '#243b55';
this.loadGraphInfo();
}
componentDidUpdate(prevProps, prevState, snapshot){
console.log(prevProps.stocktag);
console.log(prevState.stocktag);
if (prevProps.stocktag !== prevState.stocktag) {
//this.fetchData('SPY');
}
}
onSearchChange = (event) => {
var search = event.target.value;
this.setState({ stocktag: search });
}
onSubmitSearch = (e) => {
var search = this.state.searchfield;
this.setState({ stocktag: search });
}
fetchData(stock) {
//GET DATA
//UPDATE STATE
}
loadGraphInfo() {
if (this.state.stocktag == ' ') {
this.fetchData('SPY');
} else {
this.fetchData(this.state.stocktag);
}
}
render() {
return (
<div className="App" >
<Sidebar />
<Search searchChange={this.onSearchChange} submitChange={this.onSubmitSearch} />
<Graph data={this.state.data} layout={this.state.layout} />
</div>
);
}
}
export default App;
import React, { Component } from 'react';
import './Search.css'
const Search = ({ searchChange, submitChange }) => {
return (
<div>
<div class="SearchCompInput">
<input class="SearchBar" type="text" onChange={searchChange}/>
</div>
<div class="SearchCompButton">
<button class="SearchButton" onClick={submitChange}>Search</button>
</div>
</div>
);
}
export default Search;
The prevProps.stocktag is undefined because you didn't pass any props to App component. Try this in your index.js you will see preProps value but actually it does not make any sense.
render(<App stocktag='' />, document.getElementById('root'));
componentDidUpdate(prevProps, prevState, snapshot){
console.log(prevProps.stocktag);
console.log(prevState.stocktag);
if (prevProps.stocktag !== prevState.stocktag) {
//this.fetchData('SPY');
}
}
I am not quite sure on what you are trying to accomplish here but the first thing I notice is you setState of stocktag to this.state.searchfield which is ' ' in your onSubmitSearch function.
onSearchChange = (event) => {
var search = event.target.value;
this.setState({ stocktag: search });
}
onSubmitSearch = (e) => {
var search = this.state.searchfield;
this.setState({ stocktag: search });
}
Add I will also like to add that it is good practice to set value of input to a state value like so
import React, { Component, useState } from 'react';
import './Search.css'
const Search = ({ searchChange, submitChange }) => {
const [inputValue, setInputValue] = useState('')
const handleChange = (e) => {
setInputValue(e.target.value)
searchChange(e)
}
return (
<div>
<div class="SearchCompInput">
<input class="SearchBar" type="text" value = {inputValue} onChange={handleChange}/>
</div>
<div class="SearchCompButton">
<button class="SearchButton" onClick={submitChange}>Search</button>
</div>
</div>
);
}
export default Search;
I had this problem, and it was because there was a child class that was calling super.componentDidUpdate() WITHOUT passing in the parameters. So the child class looked something like:
componentDidUpdate() {
super.componentDidUpdate();
... <-- other stuff
}
And I had to change it to:
componentDidUpdate(prevProps, prevState) {
super.componentDidUpdate(prevProps, prevState);
... <-- other stuff
}
I am new in reactjs. Currently I'm developing an app which shows json COVID-19 api data into visualization using chartjs. I tried this from yesterday but I can't show the visual data.
Here is my code
App Component
import React, { useState, useEffect } from "react";
import axios from "axios";
import Chart from "./Chart";
const App = () => {
const [state, setState] = useState({});
const [loading, setLoading] = useState(true);
const [chart, setChart] = useState({});
useEffect(() => {
getData("italy");
setChart({
labels: ["Cases", "Deaths", "Recovered"],
datasets: [
{
label: "cases",
data: [state.cases]
},
{
label: "deaths",
data: [state.deaths]
},
{
label: "recovered",
data: [state.recovered]
}
]
});
}, []);
const getData = async country => {
try {
const res = await axios.get(
`https://corona.lmao.ninja/v2/historical/${country}`
);
setLoading(false);
setState(res.data.timeline);
} catch (error) {
console.log(error.response);
}
};
return (
<div>
{!loading
? console.log(
"cases",
state.cases,
"deaths",
state.deaths,
"recovered",
state.recovered
)
: null}
{!loading ? <Chart chart={chart} /> : "loading failed"}
</div>
);
};
export default App;
And Here is Chart Component
import React from "react";
import { Line } from "react-chartjs-2";
const Chart = ({chart}) => {
return (
<div>
<Line
data={chart}
height={300}
width={200}
options={{
maintainAspectRatio: false,
title: {
display: true,
text: "Covid-19",
fontSize: 25
},
legend: {
display: true,
position: "top"
}
}}
/>
</div>
);
};
export default Chart;
If I open browser and dev tools it look likes this
I want to visualize the data like this
Here is codeSandBox.io
Looks like data property within dataset takes only array of numbers. I have simplifies your code using class based component. It will help you get started.
https://codesandbox.io/s/react-chartjs-2-example-mzh9o
setChartData = () => {
let { data } = this.state;
let chartData = {
labels: ["Cases", "Deaths", "Recovered"],
datasets: [
{
label: "cases",
data: Object.values(data.cases)
},
{
label: "deaths",
data: Object.values(data.deaths)
},
{
label: "recovered",
data: Object.values(data.recovered)
}
]
};
this.setState({
chart: chartData
});
};
I have built this app using create-react-native-app, the action is dispatched but the state isn't being updated and I'm not sure why.
I see the action being logged (using middleware logger) but the store isn't getting updated, I am working on Add_Deck only for now
Here is my reducer:
// import
import { ADD_CARD, ADD_DECK } from './actions'
// reducer
export default function decks(state ={}, action){
switch(action.type){
case ADD_DECK:
return {
...state,
[action.newDeck.id]: action.newDeck
}
case ADD_CARD:
return {
...state,
[action.deckID]: {
...state[action.deckID],
cards: state[action.deckID].cards.concat([action.newCard])
}
}
default: return state
}
}
Actions file:
// action types
const ADD_DECK = "ADD_DECK";
const ADD_CARD = "ADD_CARD";
// generate ID function
function generateID() {
return (
"_" +
Math.random()
.toString(36)
.substr(2, 9)
);
}
// action creators
function addDeck(newDeck) {
return {
type: ADD_DECK,
newDeck
};
}
// export
export function handleAddDeck(title) {
return dispatch => {
const deckID = generateID();
// const newDeck = { id: deckID, title, cards: [] };
dispatch(addDeck({ id: deckID, title, cards: [] }));
};
}
function addCard(deckID, newCard) {
// { question, answer }, deckID
return {
type: ADD_CARD,
deckID,
newCard
};
}
// export
export function handleAddCard(deckID, content) {
// { question, answer }, deckID
return dispatch => {
const newCard = { [generateID()]: content };
dispatch(addCard(deckID, newCard));
};
}
And react-native component:
import React, { Component } from 'react';
import { View, Text, StyleSheet, TextInput, TouchableOpacity } from "react-native";
import {red, white} from '../utils/colors'
import { connect } from 'react-redux'
import { handleAddDeck } from '../redux/actions'
class AddDeck extends Component {
state = {
text:""
}
handleSubmit = () => {
this.props.dispatch(handleAddDeck(this.state.text))
this.setState(()=>{
return { text: ""}
})
}
render() {
return (
<View style={styles.adddeck}>
<Text> This is add deck</Text>
<TextInput
label="Title"
style={{ height: 40, borderColor: "gray", borderWidth: 1 }}
onChangeText={text => this.setState({ text })}
placeholder="Deck Title"
value={this.state.text}
/>
<TouchableOpacity style={styles.submitButton} onPress={this.handleSubmit}>
<Text style={styles.submitButtonText}>Create Deck</Text>
</TouchableOpacity>
</View>
);
}
}
function mapStateToProps(decks){
console.log("state . decks", decks)
return {
decks
}
}
export default connect(mapStateToProps)(AddDeck);
const styles = StyleSheet.create({
adddeck: {
marginTop: 50,
flex: 1
},
submitButton: {
backgroundColor: red,
padding: 10,
margin: 15,
height: 40,
},
submitButtonText: {
color: white
}
});
I guess you forgot to export your types from the actions file thus the switch(action.type) does not trigger the needed case statement.
Maybe try to add as the following:
export const ADD_DECK = "ADD_DECK";
export const ADD_CARD = "ADD_CARD";
Or further debugging just to see if the values are the ones what you are looking for:
export default function decks(state = {}, action) {
console.log({type:action.type, ADD_DECK}); // see what values the app has
// further code ...
}
I hope that helps! If not, let me know so we can troubleshoot further.