Change different Graphs based on drop down selection - reactjs

I'm working on a school project. With Reactjs. I have a side panel that includes a drop down box that gives 2 options
"live" -- display the live graph
"processed" -- show another graph created by importing a certain JSON file.
Here is the SidePanel.js I had. I do have a Graph.js for the graph, and a GraphContainer.js that basically put the SidePanel and Graph together.
I'm new to React so my code isn't good and sloppy.
If this question is a duplicate, please kindly let me know.
I'm also not a frequent on Stackoverflow, so if there's something I can do to improve my post, please let me know!
Thank you for your time!
import React, { Component } from 'react';
import './SidePanel.css'
class SidePanel extends Component {
constructor (props){
super(props)
this.state = {}
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
render() {
return(
<div className="SideContainer">
<div className="upSide">
<h4>Information</h4>
<ul>
<li>Device: <b>Device 1</b></li>
<li>Status: <b>Connected</b></li>
</ul>
</div>
<div className="downSide">
<form>
<label>
<h4>Options</h4>
<select value={this.state.value} onChange={this.handleChange}>
<option value="live">Live</option>
<option value="processed">Processed</option>
</select>
</label>
</form>
</div>
</div>
);
}
}
export default SidePanel;
So what I'm doing is I'm putting a GraphContainer.js to contain the Graph and the SidePanel, so the GraphContainer.js should not matter that much.
This is my Graph.js:
import React, { Component } from 'react';
import Highcharts from 'highcharts';
import socketIOClient from "socket.io-client";
import {
HighchartsChart, Chart, withHighcharts, XAxis, YAxis, Title, Legend, LineSeries
} from 'react-jsx-highcharts';
import './Graph.css'
var jsonData = import '../jsonData.json'
//pass the json file location
class Graph extends Component {
constructor (props) {
super(props);
this.addDataPoint = this.addDataPoint.bind(this);
const now = Date.now();
this.state = {
data: []
}
}
componentDidMount() {
//GRAPH 1: Here is to somehow put JSONdata
console.log(jsonData)
//GRAPH 2: Here is taking data from socket for testing
var sensorSerial = this.props.match.params.sensorId
const socket = socketIOClient("http://localhost:5001/test");
socket.on("newnumber", data => this.addDataPoint(data));
}
addDataPoint(data){
if(data.sensorSerial == this.props.match.params.sensorId){
var newData = this.state.data.slice(0)
console.log(new Date(data.dateTime.split(' ').join('T')))
newData.push([new Date(data.dateTime.split(' ').join('T')), data.number])
this.setState({
data: newData
})
}
}
render() {
// const {data} = this.state;
// console.log(new Date("2019-04-04T10:55:08.841287" + Z))
const plotOptions = {
series: {
pointStart: new Date("2019-04-04T10:55:08.841287Z")
}
}
return (
<div className="graph">
<HighchartsChart plotOptions={plotOptions}>
<Chart />
<Title>Data</Title>
<Legend layout="vertical" align="right" verticalAlign="middle" >
<Legend.Title>Legend</Legend.Title>
</Legend>
<XAxis type="datetime">
<XAxis.Title>Time</XAxis.Title>
</XAxis>
<YAxis>
<YAxis.Title>Y-axis</YAxis.Title>
<LineSeries name="Channel 1" data={this.state.data}/>
</YAxis>
</HighchartsChart>
</div>
);
}
}
export default withHighcharts(Graph, Highcharts);
And here is the example JSON file I want to test:
{
"data": [
{"datetime": "", "data": [1,2,3,4]},
{"datetime": "", "data": [1,2,3,4]}
]
}

You can use conditional rendering like this (Note // are comments ).
render(){
let {value } = this.state // we are using destructuring here to get the value
// property - if are you using babel , so you can
// use ES6 syntax for your Javascript code ? If not
// just use this.state.value
// from state or you could just use this.state.value
let data = value !== 'live ' ? this.getJSONdata() : this.getLiveData()
// Call functions to get the data depending on the value - see below
return (
// wherever in your jsx you want to display your graph
<Graph data={data} />
)
}
So above the render function you'd want to have the two functions you want to use getJSONdata and getLiveData
They would have the syntax below (again Im using arrow functions (ES6 syntax))
getJSONData = () => // get your data
// or not es6 sytax
getJSONData = function() {}
If you let me know how you are going to get your data I'll help you with how best to get it.

Related

ReactJS - setState() is not updating

I have a single page create-react-app project and the issue I am having is that the state is not updating. I have cut some of my code out just to make the example a bit shorter and easier to follow. The function where setState() is called is in form_change().
My goal here is to change the color of the text when there is an error, but even {this.state.test} isn't updating. I have tried putting console.log()s in various locations to get around the async nature of setState, but unfortunately they seem to show that state is never updated. It has been a while since I have used React, so there is a chance I am doing something very silly!
Thanks in advance for your help.
Here is my code:
import React,{Component} from 'react';
import './App.css';
import Dropdown from 'react-dropdown'
import classes from './classes.module.css'
import firebase from 'firebase';
import 'react-dropdown/style.css';
const axios = require('axios');
class App extends Component {
render(){
const error_empty = (param)=>{
if (this.state.error===undefined){
return false
}
else{
if (this.state.errors.find(el => el === param) === undefined){
return false
}
else return true
}
}
const form_change = (event, param)=>{
let form={...this.state.form};
form[param]=event.target.value;
let errors =verified(form);
console.log(form); //as expected
console.log(errors); //as expected
//works up til here. setState not updating for some reason.
this.setState({form:form,errors:errors,test:'Hello World'})
}
const verified = (data)=>{
let errors = [];
let form = data;
errors.push('ean')
return errors}
this.state = {
example:['abc'],
form:{
example:"abc"
}
}
return (
<div className="App">
<header className="App-header">
<div className={classes.button_holder}>
<div className={classes.page_button} onClick={()=>{null}}>
{this.state.test}
</div>
</div>
<div className={classes.user_form}>User Update Form
<div>
<input className={classes.input_text} style={{color: error_empty()?'red':'black'}} value={this.state.form.ean} onChange={(event)=>{form_change(event,'ean')}} placeholder={"EAN"}></input>
</div>
</div>
</header>
</div>
);
}
}
export default App;
Move initialization of state outside render function this.state.
Initialise your state in constructor
Don't update your state (this.setState) in render because this will lead to infinitive loop.
Move your functions error_empty(), form_change() and verified() outside the render.
To call function onChange use this
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
example:['abc'],
form:{
example:"abc"
}
error: ""
}
}
error_empty = (param) => {
if(this.state.error===undefined) {
return false
}
else {
if(this.state.errors.find(el => el === param) === undefined) {
return false
}
return true
}
}
...
render() {
...
<input ... onChange={(event) => {this.form_change(event,'ean')}}/>
...
}
}

How to filter data from an object array according to their property values, in ReactJs?

I wrote a simple code to just pass all the data taken from a firebase real time database into a react component, which is called cubetemplate, via an object array. Here is a screenshot of the firebase I used to do the test:
Here's the code I used:
<div className="gal">
{Object.keys(this.state.gallery)
.sort()
.map(item => {
//inserting into the component
return <Cubetemplate source={this.state.gallery[item]["img"]} key={item}/>;
})}
</div>
Now, the problem is that, I want to pass only the objects which verify the condition "type"="vid", into the Cubetemplate component. How do I accomplish this?
Here's the full code to take a better understanding of the situation:
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import Cubetemplate from './components/cubes/Cubetemplate';
import './Gallery.css';
import * as firebase from 'firebase';
class Gallery extends Component {
//this is the place where you declar your vars
constructor(props) {
super(props);
this.state = {
loading: false,
list: [1, 2, 3],
value: '',
gallery:{},
isLoggedIn: false,
};
this.readDB = this.readDB.bind(this); //no idea what the hell this is
}
readDB() {
var that = this;
var starCountRef = firebase.database().ref("/gallery");
//the values from the firebase will be printed in the console
starCountRef.once('value', snapshot => {
snapshot.forEach(function (childSnapshot) {
var childKey = childSnapshot.key;
var childData = childSnapshot.val();
console.log("Key: " + childKey + " Title: " + childData.title);
});
that.setState({ isLoggedIn: true });
//just to check the values of snapshot
// console.log(snapshot.val());
//takes the data from friebase to snapshot to news.
this.setState({ gallery: snapshot.val() });
console.log(this.gallery);
}, err=> {
//errors
console.log(err);
});
};
componentDidMount(){
this.readDB();
};
render() {
return (
<div className="gallerycontainer">
<div className="gal">
{Object.keys(this.state.gallery)
.sort()
//map
.map(item => {
//inserting into the component
return <Cubetemplate source={this.state.gallery[item]["img"]} key={item}/>;
})}
</div>
</div>
);
}
}
export default Gallery;
Thanks in advance stackoverflowers!
You could use firebase filtering like Frank mentioned or you could simply do some conditional rendering like this:
<div className="gal">
{Object.keys(this.state.gallery)
.sort()
.map(item => {
//Only render if the item type is 'vid'
if (this.state.gallery[item]["type"] == "vid") {
<Cubetemplate source={this.state.gallery[item]["img"]} key={item}/>;
}
})}
</div>
To load only the gallery child nodes with type equal to vid, you'd use a Firebase query like this:
var galleryRef = firebase.database().ref("/gallery");
var query = galleryRef.orderByChild("type").equalTo("vid");
query.once('value', snapshot => {
...
For more on this, I highly recommend studying the Firebase documentation on ordering and filtering data.

select tag with hashes.map function

I have a question about the getHashes(), there is a map-function that I want to list the hashes in a tag. The listItems is not defined... why?
Can I get some help with this?
import React, { Component } from 'react';
import axios from 'axios';
class Filter extends Component {
constructor(props) {
super(props);
this.state = {
hash: [
"aSj1T", "CD6oL"
]
}
}
getHashes(){
const hashes = this.state.hash;
const listItems = hashes.map((hashes) => <option>{hashes}</option>);
for (var i = 0; i < hashes.length; i++){
const hashUrl = "https://api/v1/hashes/" + hashes[i];
axios.get(hashUrl)
.then((response) => {
const data = response.data[0];
this.state.hash.push(data.hash);
})
.catch((error) => {
console.log(error);
});
}
}
render() {
return (
<div>
<select name="select"
type="select" value={this.getHashes}>
{listItems}
</select>
</div>
);
}
export default Filter;
I stripped down the code, it is with reux axios and much more on my text editor. But i hope you can run this in your text editor to help. It is not tested. If you just explain it is also cool.
1- this.state.hash.push(data.hash); is not a good idea you are mutating the state not changing it by setState
2- listItems is defined inside the function getHashes but never been added to the state or the component instance so you don't have access to it inside the render function
It is not obvious what you are trying to achieve by setting the value of select to a function

Profile Image won't display after loading from local storage using ReactJS

I'm very new to ReactJs and working on a project that uploads an image data file, converts it to a url, and stores it to localStorage. Upon choosing the file, the image is displayed. This part is working.
However, when I try to redisplay the file using localStorage.getItem("ProfileImage"), the image will not display. In addition, if I do a hard refresh even the state resets and the image preview disappears. Wondering if anyone can help tell me where I may be going wrong:
import React, { Component } from 'react'
class ProfilePhoto extends Component {
constructor(props) {
super(props)
this.state = {
imgUrl: JSON.parse(localStorage.getItem("ProfileImage")) || []
}
}
handleImageUpload = function (e) {
//get the image file
const selectedFile = e.target.files[0]
//need to convert to acceptable format for posting...
const imgData = window.URL.createObjectURL(selectedFile)
//const ActiveUser = JSON.parse(localStorage.getItem("ActiveUser"))
localStorage.setItem("ProfileImage", JSON.stringify(imgData))
this.setState({
imgUrl: imgData
})
console.log(imgData)
}.bind(this)
handleImageSave = function (e) {
e.preventDefault();
alert("Your profile image has saved!")
}.bind(this)
componentDidMount() {
let ProfileImage = JSON.parse(localStorage.getItem("ProfileImage"))
console.log(ProfileImage)
}
render() {
return (
<div className="profile-photo">
<div className="img-preview">
<img src={this.state.imgUrl} alt="" />
</div>
<form id="myPhotoForm" name="myPhotoForm" onSubmit={this.handleImageSave}>
<input type="file" id="imgUrl" name="imgUrl" onChange={this.handleImageUpload} />
<button type="submit" value="Submit!">Upload</button>
</form>
</div>
)
}
}
export default ProfilePhoto
try this
localStorage.setItem("ProfileImage", JSON.stringify(imgData).blob)

React filtering the state without ruining it

Im trying to make a search function that renders the name of the people that is matched in a search text input.
The problem is that I set the state to the items that match the search, and then the initial state is lost so no more searching can be done since the state will be empty. So how do I "fill up" the state each time?
Or maybe there is some other way without actually setting the state that im not aware of.
I tried to fix this with an attempt to reset to initial state when the handleSearch function is called right before the filter but that doesnt work.
import React from 'react';
import Header from './Header';
import peopleData from '../persons.json';
class App extends React.Component {
constructor(){
super();
this.handleSearch = this.handleSearch.bind(this);
this.state = {
people: peopleData
}
}
handleSearch(wordToMatch){
this.setState({ people: peopleData }); //Attempt to reset to initial state
const result = this.state.people.filter(d => {
const regex = new RegExp(wordToMatch, 'gi');
return d.Name.match(regex);
});
this.setState({ people: result })
}
render() {
const list = this.state.people.map((d, i) => <li key={i}>{d.Name}</li>);
return (
<div className="myApp">
<Header
tagline={"testing"}
handleSearch={this.handleSearch}
/>
<ul className="contentBody">
{list}
</ul>
</div>
)
}
}
export default App;
Component with the search input:
import React from 'react';
class Header extends React.Component {
render() {
return (
<header>
<input
type="text"
placeholder={this.props.tagline}
ref={(input) => this.searchInput = input}
onChange={() => this.props.handleSearch(this.searchInput.value)}
>
</input>
</header>
)
}
}
export default Header;
How my data looks like
[
{
"Name": "Adam",
"Born": 1971
},
{
"Name": "Bob",
"Born": 1999
},
etc etc for 20 more names
The setState function won't immediately update the state object. So when you reference this.state.people, it will reference the state prior to the setState call. You can update your code to:
handleSearch(wordToMatch) {
const result = peopleData.filter(d => {
const regex = new RegExp(wordToMatch, 'gi');
return d.Name.match(regex);
});
this.setState({
people: result
})
}
In the handleSearch set the state for the searchString variable. Then in the render method, instead of simply mapping the state, you first filter the people list, and that result is what you map.
Change:
const list = this.state.people.map((d, i) => <li key={i}>{d.Name}</li>);
into this:
const list = this.state.people.filter(d => {
const regex = new RegExp(this.state.searchString, 'gi');
return d.Name.match(regex);
}).map((d, i) => <li key={i}>{d.Name}</li>);
This way, the list in the state is left unaltered, and you filter when rendering.

Resources