React and Chartjs not updating from dynamic data through api - reactjs

I am Using React and chartjs for the project. where I receive data for chartjs from API call. There are radio buttons on screen when user selects any radio button API call is made and new data is displayed on chart. But the issue is when ever one hover over chart it toggles between previews data and recent data. I know that update() function will help it but I am not sure where to call update() in my case.
class CountByUserGraph extends React.Component {
constructor() {
super();
this.state = { selectedOption: "A" };
}
componentDidMount() {
this.makeApiCall();
}
makeApiCall = () => {
countByUserGraph(this.state.selectedOption) // API CALL
.then(response => {
// recieves data
this.getChartData(data);
})
.catch(err => console.log(err));
};
getChartData = data => {
let myChart = document.getElementById("myChart0").getContext("2d");
const CountValue = data.map(a => a.countValue);
const CountMonth = data.map(a => a.month);
var data = {
labels: CountMonth,
datasets: [
{
label: "Count",
data: CountValue
}
]
};
var option = {
responsive: true
// Options
};
let massPopChart = new Chart.Line(myChart, { data: data, options: option });
};
handleOptionChange = changeEvent => {
this.setState({ selectedOption: changeEvent.target.value }, function() {
this.makeApiCall();
});
};
render() {
return (
<div>
<form className="graph-filter">
<div>
<input
type="radio"
id="A"
name="A"
checked={this.state.selectedOption === "A"}
onClick={this.handleOptionChange}
value="A"
/>
<label htmlFor="A">A</label>
</div>
<div>
<input
type="radio"
id="B"
name="B"
checked={this.state.selectedOption === "B"}
onClick={this.handleOptionChange}
value="B"
/>
<label htmlFor="B">B</label>
</div>
</form>
<canvas id="myChart0"></canvas>
</div>
);
}
}

You can attach the map referance to the class and once data changes use this referance. Modified your code.
class CountByUserGraph extends React.Component {
this.massPopChart;
constructor() {
super();
this.state = { selectedOption: "A" };
}
componentDidMount() {
this.makeApiCall();
}
makeApiCall = () => {
countByUserGraph(this.state.selectedOption) // API CALL
.then(response => {
// recieves data
this.getChartData(data);
})
.catch(err => console.log(err));
};
getChartData = data => {
let myChart = document.getElementById("myChart0").getContext("2d");
const CountValue = data.map(a => a.countValue);
const CountMonth = data.map(a => a.month);
var data = {
labels: CountMonth,
datasets: [
{
label: "Count",
data: CountValue
}
]
};
var option = {
responsive: true
// Options
};
if(this.massPopChart){
massPopChart.data = data;
//massPopChart.config.data = data; //use this if above code does not work
massPopChart.update();
}
else{
this.massPopChart = new Chart.Line(myChart, { data: data, options: option });
}
};
handleOptionChange = changeEvent => {
this.setState({ selectedOption: changeEvent.target.value }, function() {
this.makeApiCall();
});
};
render() {
return (
<div>
<form className="graph-filter">
<div>
<input
type="radio"
id="A"
name="A"
checked={this.state.selectedOption === "A"}
onClick={this.handleOptionChange}
value="A"
/>
<label htmlFor="A">A</label>
</div>
<div>
<input
type="radio"
id="B"
name="B"
checked={this.state.selectedOption === "B"}
onClick={this.handleOptionChange}
value="B"
/>
<label htmlFor="B">B</label>
</div>
</form>
<canvas id="myChart0"></canvas>
</div>
);
}
}

you can call the update function, see the docs here
so in your codesnippet you can update the chart as shown below
let massPopChart = new Chart.Line(myChart, { data: data, options: option });
// for example
massPopChart.options.title.text = 'new title';
massPopChart.update()
similar way you can update the data also.

Related

Set value to state React js

I need a bit of help.
I am new to react, so I have stuck here. I have shared a sandbox box link. That Contains a Table. as below
| Toy | Color Available | Cost Available |
Now everything works perfectly. But I want to save the data of the table as below
The detail state should contain a list of row values of the table and the columnsValues should contain the checkbox value of Color Available and Cost Available
Example:
this.state.detail like
detail: [
{
toy : ...
color : ...
cost : ...
}
{
toy : ...
color : ...
cost : ...
}
...
...
...
]
this.state.columnsValues like
columnsValues: {
color : boolean
cost : boolean
}
Any experts please help me out. I am struggling from past few hours.
Thank you.
Sandbox link: https://codesandbox.io/s/suspicious-microservice-qd3ku?file=/index.js
just paste this code it is working .
check your console you'll get your desired output .
import React from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Table, Checkbox, Input } from "antd";
import { PlusCircleOutlined, MinusCircleOutlined } from "#ant-design/icons";
const { Column } = Table;
class ToyTable extends React.Component {
constructor(props) {
super(props);
this.state = {
dataSource: [
{
key: 0,
toy: "asdf",
color: "black",
cost: "23"
}
],
count: 0,
colorSwitch: false,
costSwitch: false,
columnsValues: {
color: true,
cost: true
},
detail: []
};
}
componentDidMount(){
const count = this.state.dataSource.length;
this.setState({
count
})
}
handleAdd = () => {
const { dataSource } = this.state;
let count = dataSource.length;
const newData = {
key: count,
toy: "",
color: "",
cost: ""
};
this.setState({
dataSource: [...dataSource, newData],
count
});
};
handleDelete = key => {
const dataSource = [...this.state.dataSource];
this.setState({ dataSource: dataSource.filter(item => item.key !== key) });
};
onChangecolor = (e, record) => {
let dataSource = this.state.dataSource;
let key = record.key;
dataSource[key].color = e.target.value;
this.setState({
dataSource
});
};
onChangeCost = (e, record) => {
let dataSource = this.state.dataSource;
let key = record.key;
dataSource[key].cost = e.target.value;
this.setState({
dataSource
});
};
onChangeToy = (e, record) => {
console.log("I am inside handleInputChange", e.target.value, record);
let dataSource = this.state.dataSource;
let key = record.key;
dataSource[key].toy = e.target.value;
this.setState({
dataSource
});
};
checkColor = e => {
this.setState({ colorSwitch: e.target.checked });
};
checkCost = e => {
this.setState({ costSwitch: e.target.checked });
};
render() {
const { dataSource } = this.state;
console.log(dataSource);
return (
<Table bordered pagination={false} dataSource={dataSource}>
<Column
title="Toy"
align="center"
key="toy"
dataIndex="toy"
render={(text, record) => (
<Input
component="input"
className="ant-input"
type="text"
value={record.toy}
onChange={e => this.onChangeToy(e, record)}
/>
)}
/>
<Column
title={() => (
<div className="row">
Color Available
<div className="col-md-5">
<Checkbox size="small" onChange={this.checkColor} />
</div>
</div>
)}
align="center"
dataIndex="color"
render={(text, record) => (
<Input
disabled={!this.state.colorSwitch}
value={record.color}
onChange={e => this.onChangecolor(e, record)}
component="input"
className="ant-input"
type="text"
/>
)}
/>
<Column
title={() => (
<div className="row">
Cost Available
<div className="col-md-5">
<Checkbox size="small" onChange={this.checkCost} />
</div>
</div>
)}
align="center"
dataIndex="color"
render={(text, record) => (
<Input
disabled={!this.state.costSwitch}
value={record.cost}
onChange={e => this.onChangeCost(e, record)}
component="input"
className="ant-input"
type="text"
/>
)}
/>
<Column
render={(text, record) =>
this.state.count !== 0 && record.key + 1 !== this.state.count ? (
<MinusCircleOutlined
onClick={() => this.handleDelete(record.key)}
/>
) : (
<PlusCircleOutlined onClick={this.handleAdd} />
)
}
/>
</Table>
);
}
}
ReactDOM.render(<ToyTable />, document.getElementById("container"));
This isn't an exact answer, but just as a general direction - you need something in the state to capture the values of the currently edited row contents, that you can then add to the final list. This is assuming once committed, you don't want to modify the final list.
Firstly, have an initial state that stores the values in the current row being edited
this.state = {
currentData: {
toy: '',
color: '',
..other props in the row
}
...other state variables like dataSource etc
}
Secondly, when the value in an input box is changed, you have to update the corresponding property in the currentData state variable. I see that you already have a handleInputChange function
For eg, for the input box corresponding to toy, you'd do
<input onChange={e => handleInputChange(e, 'toy')} ...other props />
and in the function itself, you'd update the currentData state variable, something like:
handleInputChange = (e, property) => {
const data = this.state.currentData
data[property] = e.target.value
this.setState({ currentData: data })
}
Finally, when you press add, in your handleAddFunction, you want to do two things:
1) use the currentData in state, that's been saving your current values and push them into the dataSource or details array
2) restore the currentData to the blank state, ready to track updates for the next row.
handleAdd = () => {
const { count, dataSource } = this.state;
const newData = {
key: count,
...this.state.newData,
};
this.setState({
dataSource: [...dataSource, newData],
count: count + 1,
currentData: {
toy: '',
// other default values
}
});
};

Geocoding API - address not updating to lat long instead persisting to database as postcode text

I am trying to use the Google geocode api to turn a postcode into lat long coordinates.
It seems to be working when I console.log.state, however the postcode text i.e 'TW135QZ' is being persisted into the database instead of the lat long coordinates.
Can anybody see why the lat long cords are not being persisted? I am stuck now >.<
Main bit of code:
onCreateCat = (e, authUser) => {
Geocode.fromAddress(this.state.address).then(
response => {
const { lat, lng } = response.results[0].geometry.location;
this.setState({address: lat + ',' + lng});
},
error => {
console.error(error);
}
)
.then(
this.props.firebase.cats().push({
text: this.state.text,
image: 'https://images.unsplash.com/photo-1518791841217-8f162f1e1131?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9',
userId: authUser.uid,
address: this.state.address,
})
)
e.preventDefault();
}
Fullcode:
const INITIAL_STATE = {
text: '',
address: ''
}
class AddCat extends React.Component {
constructor(props) {
super(props);
this.state = {
...INITIAL_STATE
}
}
componentDidMount() {
// set Google Maps Geocoding API for purposes of quota management. Its optional but recommended.
Geocode.setApiKey(process.env.REACT_APP_GOOGLEKEY);
// set response language. Defaults to english.
Geocode.setLanguage("en");
// set response region. Its optional.
// A Geocoding request with region=es (Spain) will return the Spanish city.
Geocode.setRegion("es");
// Enable or disable logs. Its optional.
Geocode.enableDebug();
// Get latidude & longitude from address.
}
onCreateCat = (e, authUser) => {
Geocode.fromAddress(this.state.address).then(
response => {
const { lat, lng } = response.results[0].geometry.location;
this.setState({address: lat + ',' + lng});
},
error => {
console.error(error);
}
).then(
this.props.firebase.cats().push({
text: this.state.text,
image: 'https://images.unsplash.com/photo-1518791841217-8f162f1e1131?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9',
userId: authUser.uid,
address: this.state.address,
})
)
e.preventDefault();
}
onChangeText = e => {
this.setState({ text: e.target.value });
};
onChangeAddress = e => {
this.setState({ address: e.target.value });
};
render() {
const { text, address } = this.state;
console.log(this.state);
return (
<div>
<h1>Add cat</h1>
<AuthUserContext.Consumer>
{authUser => (
<div>
<form onSubmit={e => this.onCreateCat(e, authUser)}>
<input
type="text"
value={text}
onChange={this.onChangeText}
placeholder="Cats Name"
/>
<input
name="address"
value={address}
onChange={this.onChangeAddress}
type="text"
placeholder="Cats Postcode">
</input>
<button type="submit">Send</button>
</form>
</div>
)}
</AuthUserContext.Consumer>
</div>
);
}
}
const condition = authUser => !!authUser;
export default withAuthorization(condition)(AddCat);

How to mark dynamically created option as used in options list

I want to render options in select dynamically from the list.
If option already used I want to mark it as isUsed: true.
I mean change state of <Wrapper />
this.state = {
options: {
One: {value: "one", isUsed: false},
Two: {value: "two", isUsed: false},
Three: {value: "three", isUsed: false}
}
What is the best way?
I'm trying to mark it using componentDidMount() using markUsed() (for testing purpose there is static key "One"), but how do I get the current mounted option, to mark dynamic key in this.state?
I've tried to console.log(this) in componentDidMount(), but it seems like it doesn't contain current mounted option value.
import React from 'react';
import ReactDOM from 'react-dom';
class Input extends React.Component {
componentDidMount = () => {
console.log(this);
this.props.markUsed();
}
render() {
let options = Object.keys(this.props.list).map((item,i) => {
if (!this.props.list[item].isUsed) {
return(
<option key={i} value={this.props.list[item].value}>{item}</option>
)
}
});
return (
<div>
<select>
{options}
</select>
<input type="text" />
</div>
)
}
}
class Wrapper extends React.Component {
constructor(props) {
super(props);
this.markUsed = this.markUsed.bind(this);
this.state = {
options: {
One: {value: "one", isUsed: false},
Two: {value: "two", isUsed: false},
Three: {value: "three", isUsed: false}
},
inputs: []
}
}
markUsed = () => {
this.setState(prevState =>({
options: {
...prevState.options,
One: {
...prevState.options.One,
isUsed: true
}
}
}));
}
addInput = (e) => {
this.setState((prevState) => ({
inputs: [...prevState.inputs, {option: "", value: ""}],
}));
}
render() {
return(
<div>
{
this.state.inputs.map((val, idx) => {
return (
<div key={idx}>
<Input list={this.state.options} markUsed={this.markUsed} />
</div>
)
})
}
<button type="button" onClick={this.addInput}>add</button>
</div>
)
}
}
ReactDOM.render(<Wrapper />, document.getElementById('root'));
Here is a generic solution where you pass a target to be marked:
markUsed = target => {
const [key, keyValue] = Object.entries(this.state.options).find(
([, keyValue]) => keyValue.value === target
);
keyValue.isUsed = true;
this.setState(prevState => ({
...prevState,
options: { ...prevState.options, [key]: keyValue }
}));
};
Therefore you need to pass a target value like one,two,three etc.
class Input extends React.Component {
state = {
...
};
render() {
const { markUsed } = this.props;
const options = Object.keys(this.props.list).map((item, i) => {
if (!this.props.list[item].isUsed) {
if (this.props.list[item].value === this.state.currValue) {
markUsed(this.state.currValue);
}
return ...
}
});
return (
<>
<select
value={this.state.value}
onChange={e => {
e.persist();
const currValue = e.target.value;
markUsed(currValue);
this.setState({ currValue });
}}
>
...
</>
);
}
}

Load data into inputs when entering the code

I have updated the Code.
Here I have a functional Select Autocomple showing the list of records from DB "Register". When selecting a Code, the Name value is automatically renamed.
The same thing I want to do but with the not with , I want to call more than two values like this in the image and in select is only Label and Value
Capture: [1]: https://i.stack.imgur.com/ELf1a.png
class Register extends Component {
state = {
status: "initial",
data: [],
name:'',
code:''
}
componentDidMount = () => {
this. getInfo()
}
getInfo= async () => {
try {
const response = await getAll('register')
console.log(response.data)
this.setState({
status: "done",
data: response.data
});
} catch (error) {
this.setState({
status: "error"
});
}
};
handleChange = (selectedOption) => {
this.setState({
selectedOption,
name: selectedOption.value
});
render() {
//show Name and code on Select from Register
const data = this.state.data.map( st => ({value: st.Name, label: st.Code}));
return (
<Container>
<RowContainer margin="1px" >
<ColumnContainer margin="10px">
<h3>Info</h3>
<label>Code</label>
<Select
width='215px'
value={selectedOption}
onChange={this.handleChange}
options={data}
name={"Code"}
/>
<label>Name</label>
<Input
width='150px'
type="text"
name={"Name"}
placeholder="Name"
value={this.state.name} />
</ColumnContainer>
</RowContainer>
</Container>
)
}
};
export default Register;
You want to know how change the state for <input/>
try this
constructor(props){
super(props)
this.state = {
status: "initial",
data: [],
codigo: "",
nombre: ""
}
}
handleChange(event){
let stateUpdate = this.state;
stateUpdate[event.target.name] = event.target.value}
this.setState(stateUpdate);
}
render() {
const data = [...this.state.data];
return (
<Container>
<RowContainer margin="1px" >
<ColumnContainer margin="10px">
<h3>Info</h3>
<label>Codigo</label>
<Input
name="codigo"
width='150px'
type="text"
placeholder="Digite el codigo"
value={data.codigo } ref="codigo" />
<label>Nombre</label>
<Input
name="nombre"
width='150px'
type="text"
placeholder="Nombre completo"
value={this.state.nombre} />
</ColumnContainer>
</RowContainer>
</Container>
)
}

react update screen after changing values in the state

I am new to react and I am working on a project where I was ask to reset a form to its defaults.
I created a function that gets call after I click the reset button
<input id="reset_button"
type="button"
name="reset"
value="Reset"
onClick={this.resetSearch}/>
This is my function:
resetSearch: function() {
this.setState({ID: 'Moo'});
},
I do see the ID change value in the console but it does not update on the screen.
Other things that I have tried
# when I do this the element despairs from then screen
resetSearch: function() {
var values = this.fields.state.values;
this.setState({
defaultValues: {
values
},
ignoreDefault: false
});
}
#render function
render: function() {
return (
<div className="card-body with-padding-bottom-0">
<form id={this.formId}>
<div id="sn-fields" className="usa-grid-full sn-search">
<SNFields ref={(fields) => { this.fields = fields; }} ddl_id='sn_search_card_type' snOptions={ this.getProp('snOptions')} fields={this.getProp('fields')} updateParentState={this.updateStateByField} defaultFieldValues={this.getProp('defaultValues')} ignoreDefault={this.state.ignoreDefault}></SNFields>
</div>
<div className="usa-grid-full with-margin-top-10 validation-div">
<div id="sn_search_card_search_button_container" className="usa-width-one-whole">
<label htmlFor="system_validator"></label>
<input hidden name="system_validator" id="system_validator"/>
<input id="search_button" type="button" name="search" value="Search" onClick={this.personSearch}/>
<input id="reset_button" type="button" name="reset" value="Reset" onClick={this.resetSearch}/>
</div>
</div>
</form>
</div>
);
}
I was able to find a class SNFields
var SNFields = React.createClass({
filterFields: function(searchVal) {
console.log('PCQSFields - filterFields ')
var filterLabels = [];
//filter in this component since the filtering can't be done on the ruby side
switch(searchVal) {
case 'APPLICATION_ID':
case 'ENUMERATOR':
case 'ENCOUNTER_ID': {
filterLabels = ['ID'];
break;
}
case 'NAME_AND_DOB': {
filterLabels = ['Date of Birth', 'Last Name', 'Date Range', 'First Name'];
break;
}
default: {
break;
}
}
var fields = this.props.fields.slice();
for (var i = fields.length - 1; i > -1; i--) {
if (filterLabels.indexOf(fields[i].label) < 0) {
fields.splice(i, 1);
}
}
return fields;
},
render: function() {
console.log('NSFields - render ')
return (
<div>
<div className="usa-width-one-third">
<label htmlFor={this.props.ddl_id} className="card-label bold">Search Type</label>
<Dropdown id={this.props.ddl_id} onChange={this.updateFields} selectableArray={this.props.nsOptions} classes="" selectedOption={this.state.ddl}/>
</div>
<div className="flex-container" style={{'flexWrap': 'row'}}>
{this.nsFieldsHelper(this.state.fields)}
</div>
</div>
);
}
});
I guess what I really want to do is when I press the reset to call
SNFields.filterFields('NAME_AND_DOB')
but when I try that I get a message in the console that reads: Uncaught TypeError: NSFields.filterFields is not a function
How does your componentDidMount() and componentWillReceiveProps(newProps) look like?
This is how I have done an Input component:
import React, { Component } from 'react';
export default class Input extends Component {
displayName: 'Input';
constructor(props) {
super(props);
this.state = {
value: this.props.value,
disabled: this.props.disabled,
checked: this.props.checked,
className:this.props.className,
maxLength:this.props.maxLength,
placeholder:this.props.placeholder,
id:this.props.id,
name:this.props.name,
type:this.props.name,
oldValue:this.props.value,
backgroundColor:''
};
this.handleBlur = this.handleBlur.bind(this);
this.handleChange = this.handleChange.bind(this);
};
componentWillReceiveProps(nextProps) {
if (this.state.value !== nextProps.value) {
this.setState({ value: nextProps.value});
};
if (this.state.disabled !== nextProps.disabled) {
this.setState({ disabled: nextProps.disabled});
};
if (this.state.checked !== nextProps.checked) {
this.setState({ checked: nextProps.checked});
};
if (this.state.className !== nextProps.className) {
this.setState({ className: nextProps.className});
};
if (this.state.maxLength !== nextProps.maxLength) {
this.setState({ maxLength: nextProps.maxLength});
};
if (this.state.placeholder !== nextProps.placeholder) {
this.setState({ placeholder: nextProps.placeholder});
};
};
componentDidMount() {
this.setState({ value: this.props.value,
disabled: this.props.disabled,
checked: this.props.checked,
className:this.props.className,
maxLength:this.props.maxLength,
placeholder:this.props.placeholder
});
};
handleBlur(event) {
if ((this.props.checkError===null)||(this.props.checkError(event,false) === true)) {
this.setState({ value: event.target.value,
oldValue: event.target.value
})
}
else
{
this.setState({ value: this.state.oldValue })
}
this.setState({ backgroundColor: ''})
};
handleChange(event) {
if (this.state.value !== event.target.value) {
this.setState({ value: event.target.value })
if ((this.props.checkError!==null)&&(this.props.checkError(event,true) === false)) {
this.setState({ backgroundColor: 'red'})
}
else
{
this.setState({ backgroundColor: ''})
}
}
if (this.props.onClick!==null) {
this.props.onClick();
}
};
render() {
return <input value={this.state.value}
maxLength={this.state.maxLength}
placeholder={this.state.placeholder}
className={this.state.className}
id={this.props.id}
name={this.props.name}
type={this.props.type}
disabled={this.state.disabled}
checked={this.state.checked}
onBlur={this.handleBlur}
onChange={this.handleChange}
style={{background:this.state.backgroundColor}}/>
}
};
Input.propTypes=
{
value:React.PropTypes.string,
placeholder:React.PropTypes.string,
maxLength: React.PropTypes.number,
disabled:React.PropTypes.bool,
checked:React.PropTypes.bool,
className:React.PropTypes.string,
id:React.PropTypes.string,
name:React.PropTypes.string,
type:React.PropTypes.string,
checkError: React.PropTypes.func,
onClick: React.PropTypes.func
}
Input.defaultProps =
{
placeholder:'',
maxLength:100,
disabled:false,
checked:false,
value:'',
className:'',
id:'',
name:'',
type:'text',
checkError:null,
onClick:null
}

Resources