React Component doesn't update on updating the state - javascript

Map.js
import React, { Component } from "react";
import DatamapsIndia from "react-datamaps-india";
class Map extends Component {
state = { };
render() {
console.log("checkbox value",this.props.checkbox) //correctly updating on clicking on checkbox
return (
this.props.checkbox ?
<DatamapsIndia
regionData={{
Maharashtra: {
value: 10,
},
}}
// hoverComponent={({ value }) => {
// return <span>{value}</span>;
// }}
mapLayout={{
title: "Covid Map",
legendTitle: "Active Cases",
startColor: "#FFDAB9",
endColor: "#FF6347",
hoverTitle: "Count",
noDataColor: "#f5f5f5", // this is the variable that has to change on changing of checkbox value
borderColor: "#8D8D8D",
hoverBorderColor: "#8D8D8D",
hoverColor: "green",
}}
/> :
<DatamapsIndia
regionData={{
Maharashtra: {
value: 10,
},
}}
// hoverComponent={({ value }) => {
// return <span>{value}</span>;
// }}
mapLayout={{
title: "Covid Map",
legendTitle: "Active Cases",
startColor: "#FFDAB9",
endColor: "#FF6347",
hoverTitle: "Count",
noDataColor: "#f24f22", // this is the variable that has to change on changing of checkbox value
borderColor: "#8D8D8D",
hoverBorderColor: "#8D8D8D",
hoverColor: "green",
}}
/>
);
}
}
export default Map;
App.js
import logo from "./logo.svg";
import "./App.css";
import React, { Component } from "react";
import Header from "./Components/Header";
import Body from "./Components/Body";
import Map from "./Components/Map";
class APP extends Component {
state = {
checkbox: false,
};
constructor(props) {
super(props);
this.checkBoxfunction = this.checkBoxfunction.bind(this);
}
checkBoxfunction() {
var check = document.getElementById("customSwitch2").checked;
this.setState(
{
checkbox: check,
},
() => {
console.log(this.state.checkbox);
}
);
}
componentDidMount() {
console.log("CheckBox Updation(APP.JS) Mount", this.state.checkbox);
this.checkBoxfunction();
}
render() {
return (
<div className="App">
<Header
checkbox={this.state.checkbox}
checkBoxfunction={this.checkBoxfunction}
/>
<div className="d-flex flex-column">
<div className="map" style={{ width: "70%" , position:"absolute", right:"10px", top:"130px"}}>
<Map checkbox={this.state.checkbox} />
</div>
<div className="table" style={{ width: "30%"}}>
<Body checkbox={this.state.checkbox} />
</div>
</div>
</div>
);
}
}
export default APP;
Here I'm passing the checkbox value to Map component and I want that whenever the state of the checkbox changes update the "NoDatacolor" value but the thing is not In the way I want .
Even on changing the checkbox state my map "noDataColor" remains the same but the console line in Map component updating the correct value of checkbox each and everytime I'm clicking on it.

It's possible that the <DatamapsIndia {...args} /> handle its props as immutable, I worked with leaflet and faced a similar issue, here you can find information about it.
You can try the following dirty solution:
const { checkbox } = this.props;
...
<DatamapsIndia
...
key={`${checkbox}`}
...
mapLayout={{
...
noDataColor: checkbox ? "#f5f5f5" : "#f24f22",
...
}}
/>
hope this works, please let me know.

Related

How to work with objects in an array in another document?

I need to display the title object from the array in the document App.js.
Everything works when I use an array without any objects :
(before)
App.js:
import React from 'react'
import TodoList from './Todo/TodoList'
function App(props) {
const mass = [
"text1",
"text2",
"text3"
]
return (
<div className='wrapper'>
<TodoList name={mass}> </TodoList>
</div>
)
}
export default App;
But when I add couple objects to array, there is an error (Objects are not valid as a React child (found: object with keys {completed, title}). If you meant to render a collection of children, use an array instead.)
(after)
App.js:
import React from 'react'
import TodoList from './Todo/TodoList'
function App(props) {
const mass = [
{completed: false, title: "text1"},
{completed: false, title: "text2"},
{completed: false, title: "text3"}
]
return (
<div className='wrapper'>
<TodoList name={mass}> </TodoList>
</div>
)
}
export default App;
TodoList.js:
import React from 'react'
import TodoItem from './TodoItem'
const styles = {
ul: {
color: 'red',
margin: 0,
padding: 0,
}
}
export default function TodoList(props) {
return (
<ul style={
styles.ul
}>
<TodoItem name2={props.name}/>
</ul>
)
}
TodoItem.js:
import React from 'react'
import App from '../App'
const styles = {
btn: {
background: 'none',
marginLeft: '30px',
},
divv: {
listStyleType: 'none',
margin:'10px',
marginLeft: '10px',
border:'1px solid #ccc',
padding:'5px',
width: '250px'
}
}
const TodoItem = (props) => {
return (
<div>
{
props.name2.map((comp, momp) =>
{
return (
<div style={styles.divv}>
<li>
<input type="checkbox" /> {momp +1} Test {comp} <button style={styles.btn}>×</button>
</li>
</div>
)
}
)
}
</div>
)
}
export default TodoItem
So in the TodoItem doc I want to work with object named 'title' from array, but not with an array as the whole
try to use comp.title instead of comp in your TodoItem.js file
when you use array with string items in map function you will see only string (items of array)
but when you use object in your array in map function you will see objects as item

How do I use state and the value of an object's keys to dynamically style in React?

Im using CRA and Axios here, the code will be included.
Problem: I want to make a stock chart, a very basic one. it should function as such, on load component mounts and axios makes a call to the static api (for now) to populate the chart of exchange rates. as a basic learning point for me to dive deeper im stuck on how to use the value of each key in the object to make it red if its worht less than a usd and green if its worth more. I know how to refer to the styling in line but dont know how to access the value of the keys in state setting context.. thanks
postman response from api looks like such and populates in the container properly
{
"base": "USD",
"rates": {
"GBP": 0.7775366251,
"HKD": 7.8246518358,
"IDR": 14084.997287032,
"ILS": 3.4664496292,
"DKK": 6.7577319588,
"INR": 71.6865617652,
"CHF": 0.994212335,
"MXN": 19.3951890034,
"CZK": 23.0719840839,
"SGD": 1.3629951167,
"THB": 30.1953336951,
"HRK": 6.7249954784,
"EUR": 0.9043226623,
"MYR": 4.1710074154,
"NOK": 9.1411647676,
"CNY": 7.0387050099,
"BGN": 1.768674263,
"PHP": 50.8545849159,
"PLN": 3.8865075059,
"ZAR": 14.7149574968,
"CAD": 1.327455236,
"ISK": 123.259178875,
"BRL": 4.188189546,
"RON": 4.3173268222,
"NZD": 1.5586905408,
"TRY": 5.7079942123,
"JPY": 108.545849159,
"RUB": 63.6969614759,
"KRW": 1178.395731597,
"USD": 1.0,
"AUD": 1.4728703201,
"HUF": 302.3060227889,
"SEK": 9.6097847712
},
"date": "2019-11-22"
}
React code
import React, { Component } from "react";
import {Link} from "react-router-dom";
import './currency.css';
import { Container, Row, Col } from 'reactstrap';
import axios from'axios'
class Currency extends Component {
constructor(props){
super(props);
this.state = {
subject: "react state",
instructor: "Lukas",
purpose: "to make stacks",
data: {},
isGreater: false,
isLess: false,
isSpecial: false
// queryUrl: "https://api.exchangeratesapi.io/latest?base=USD"
};
}
componentDidMount(){
axios.get('https://api.exchangeratesapi.io/latest?base=USD')
.then(response => {
if (response.data.rates.hasSpecialStuff) {
this.setState({
data: response.data.rates,
isSpecial: true
})
}
this.setState({
data: response.data.rates
})
})
}
render() {
return (
<div>
<Container>
<div id="ratesDiv">
<h2> Current Exchange Rates</h2>
{Object.keys(this.state.data).map(key => <p className={'rateP'} style={this.state.isSpecial ? {backgroundColor: "red"} : {backgroundColor: "transparent"}}>{`${key}, ${this.state.data[key]}`}</p>)}
</div>
</Container>
</div>
);
}
}
export default Currency;
We can set the style as below
{Object.keys(this.state.data).map(s =>
<p
style={this.state.data[s] > 1 ?
{ backgroundColor: 'green' } : { backgroundColor: 'red' }
}>
{s}, {this.state.data[s]}
</p>)}
According to your code isSpecial should remain always either true or false, so my understanding is that it shouldn't be part of the condition to make everything red or transparent rather you should focus on USD which is also your requirement too. You can try this following code:
{Object.keys(this.state.data).map((key, i) => (
<p
key={i}
className={"rateP"}
style={
parseFloat(this.state.data[key]) <
parseFloat(this.state.data["USD"])
? { backgroundColor: "red" }
: { backgroundColor: "transparent" }
}
>{`${key}, ${this.state.data[key]}`}</p>
))}

React JS How to change font color in element other than clicked button onClick?

So I have managed to change the background color of a button using setState() within that button. However, I am trying to use that button to change the font color of list elements within the same component.
Using setState() only lets me change the element I am clicking. I've tried querySelecting the class of the other elements, but using left.setState() is not a valid function.
How can I change the CSS properties of an element using an onClick function of a button?
import React, { Component } from 'react';
import firebase from 'firebase';
import { firebaseConfig } from './connection';
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
let messageRef = firebase.database().ref('messages');
class LandingPage extends Component {
constructor(props) {
super(props);
this.state = {
name: '',
message: '',
list: [],
font: "black",
color: "blue"
}
}
// onChange = () => {
// if (this.state.color == 'blue'){
// this.setState({ color: 'green' });
// }
// else {
// this.setState({ color: 'blue' });
// }
// }
onChange = () => {
var left = document.querySelectorAll(".left");
if (this.state.color === 'black'){
this.setState({ color: 'grey' });
}
else {
this.setState({ color: 'black' });
}
}
render() {
return <div className='container'>
{/* title */}
<div className='titleDiv'>
<h1>React Message App</h1>
</div>
{/* messages will be listed here */}
<div className='messagesDiv' id='messagesDivId'>
<ul>
{/* List array is mapped through*/}
{this.state.list.map(item => {
return (
<li className={(item.name === this.state.name ? 'right' : 'left')}
style={{ color: this.state.font }}
key={item.id}
id={item.id}>
{item.name}: {item.message}
</li>
)
})}
</ul>
</div>
{/*think, delete options*/}
<button className='button think' style={{ backgroundColor: this.state.color }} onClick={this.onChange}>Think...</button>
<button className='button delete'>Delete last message</button>
</div>
}
}
export default LandingPage;
It is the 'think' button which should be clicked to change the list elements with a 'left' or 'right' class name. Please advise...
You messed up some variable names and misunderstood how React works.
First, you can't query and HTML element and execute setState because this is a React function. This function is not accessible from within the HTML document.
Second, your first approach with changing a state variable with the button click and mapping this variable to the color of the list elements is correct, but you mixed up the names:
This is your onChangeMethod:
onChange = () => {
if (this.state.color == 'blue'){
this.setState({ color: 'green' });
}
else {
this.setState({ color: 'blue' });
}
}
Here you are mapping the state variable to the color property:
<li className={(item.name === this.state.name ? 'right' : 'left')}
style={{ color: this.state.font }}
key={item.id}
id={item.id}>
{item.name}: {item.message}
</li>
You are setting state.color in theonChange function, but you are referencing state.font in you list element, instead change style to the following:
style={{ color: this.state.color }}
You need to do the binding to the onChange method. You can do it in the constructor method like this:
constructor(props) {
super(props);
this.state = {
name: '',
message: '',
list: [],
font: "black",
color: "blue"
}
this.onChange = this.onChange.bind(this)
}
import React, { Component } from "react";
class LandingPage extends Component {
constructor(props) {
super(props);
this.state = {
list: [
{
id: "1",
message: "Hello World 1"
},
{
id: "2",
message: "Hello World 2"
},
{
id: "3",
message: "Hello World 3"
}
],
color: "red"
};
this.onChange = this.onChange.bind(this);
}
onChange = () => {
if (this.state.color == "red") {
this.setState({ color: "green" });
} else {
this.setState({ color: "red" });
}
};
render() {
return (
<div className="container">
<div className="titleDiv">
<h1>React Message App</h1>
</div>
<div className="messagesDiv" id="messagesDivId">
<ul>
{this.state.list.map(item => {
return (
<li
style={{ color: this.state.color }}
key={item.id}
id={item.id}
>
{item.message}
</li>
);
})}
</ul>
</div>
<button className="button think" onClick={this.onChange}>
Change Color
</button>
</div>
);
}
}
export default LandingPage;
Check whether this is what you want?
if you want to try inline..
<button className='button think' style={{ backgroundColor: this.state.color }} onClick={()=>{this.state.this.state.color == 'blue'?this.setState({ color: 'green' }):this.setState({ color: 'blue' })}}>Think...</button>

How can a React method bound in constructor lose it's boundedness when passed as parameter?

Here is the codesandbox for this question: https://codesandbox.io/s/rdg-grouping-81b1s
I am using React-Data-Grid to render a table. I render a ReactDataGrid with two columns, and When you click on the text GROUP in a header cell you group by that column.
To be able to have a custom header cell with that text GROUP, I use the property headerRenderer in the object defining the columns.
The value passed to this property is a function that takes an onClick handler as parameter, and returns a functional React component that uses that onClick handler.
The onClick parameter is just a method on the original React component, and it is bound in the component's constructor.
As you can see, I am using this headerRenderer property twice, once for each column. However, for the first column, I bind the parameter function to the React component again. For the second column I do not, and this generates an error when I try to click the GROUP text for this column. See error image further below.
My question is: why do I have to bind given that I've already bound the function in the constructor?
import React from 'react';
import './App.css';
import ReactDataGrid from 'react-data-grid';
import { Data } from 'react-data-grid-addons';
const HeaderRowRenderer = function(props) {
return (
<div
style={{
backgroundColor: 'red',
paddingLeft: 10,
height: '100%',
padding: 0,
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
}}
>
<span>{props.column.name}</span>
<span onClick={props.onClick}>GROUP</span>
</div>
);
};
const HeaderRenderer = function(groupBy, onClick) {
return function(props) {
return (
<HeaderRowRenderer
{...props}
onClick={function() {
onClick(groupBy);
}}
/>
);
};
};
const rows = [{ productname: 'Beef', quantity: 5 }, { productname: 'Veggies', quantity: 10 }];
class App extends React.Component {
columns = [
{
key: 'productname',
name: 'Product',
width: 200,
headerRenderer: HeaderRenderer('productname', this.groupBy.bind(this)),
},
{
key: 'quantity',
name: 'Quantity',
headerRenderer: HeaderRenderer('quantity', this.groupBy),
},
];
constructor(props) {
super(props);
this.state = {
groupBy: new Set([]),
};
this.groupBy = this.groupBy.bind(this);
}
groupBy(group) {
const newSet = new Set(this.state.groupBy);
if (newSet.has(group)) {
newSet.delete(group);
} else {
newSet.add(group);
}
this.setState({ groupBy: newSet });
}
render() {
const groupBy = Array.from(this.state.groupBy);
// const rows = this.props.orderItems;
const groupedRows = Data.Selectors.getRows({
rows: rows,
groupBy,
});
return (
<div>
<ReactDataGrid
columns={this.columns}
rowGetter={i => groupedRows[i]}
rowsCount={groupedRows.length}
minHeight={650}
/>
</div>
);
}
}
export default App;
I looked at the code for React-Data-Grid, and I believe that the headerRenderer prop is called as below:
getCell() {
const { height, column, rowType } = this.props;
const renderer = this.props.renderer || SimpleCellRenderer;
if (isElement(renderer)) {
// if it is a string, it's an HTML element, and column is not a valid property, so only pass height
if (typeof renderer.type === 'string') {
return React.cloneElement(renderer, { height });
}
return React.cloneElement(renderer, { column, height });
}
return React.createElement(renderer, { column, rowType });
}
I'm not very familiar with the ways in which a function that was bound using bind and then is passed around can lose this boundedness. Does this happen as a result of React.cloneElement, or what could be the cause of it?

Toggle active class on child components

I'm having a bit of a head ache trying to figure out the React way of implementing this.
I have a Searches component which houses SearchItems, when an item is clicked among other things I need to set it's state to active to that it gets the correct CSS, I managed to get this working fine but how would I go about removing the active state from the others?
I was thinking that I could pass down a function from the top level component that would take the ID of the search, when clicked it'd zip through SearchItems and change their state to either true/false depending on which ID it was?
Code below!
Top level component:
import React from "react";
import {Link} from "react-router";
import Search from "./Search";
export default class Searches extends React.Component {
constructor(){
super();
this.state = {
searches : [
{
id : "2178348216",
searchName: "searchName1",
matches: "5"
},
{
id : "10293840132",
searchName: "searchName2",
matches: "20"
}
]
};
}
render() {
const { searches } = this.state;
const SearchItems = searches.map((search) => {
return <Search key={search.id} {...search}/>
})
return (
<div> {SearchItems} </div>
);
}
}
Search items component
export default class Search extends React.Component {
constructor() {
super();
// Set the default panel style
this.state = {
panelStyle: { height: '90px', marginBottom: '6px', boxShadow: '' },
selected: false
}
}
isActive(){
return 'row panel panel-success ' + (this.state.selected ? 'active' : 'default');
}
viewNotifications(e){
this.setState({selected: true});
}
render() {
const { id, searchName, matches } = this.props;
const buttonStyle = {
height: '100%',
width: '93px',
backgroundColor: '#FFC600'
}
return (
<div style={this.state.panelStyle} className={this.isActive()}>
<div class="col-xs-10">
<div class="col-xs-7">
Search Name: {searchName}
</div>
<div class="col-xs-7">
Must Have: PHP, MySQL
</div>
<div class="col-xs-7">
Could Have: AngularJS
</div>
</div>
<button type="button" onClick={this.viewNotifications.bind(this)} style={buttonStyle} class="btn btn-default btn-lg"> {matches} </button>
</div>
);
}
}
I think you don't need the state in the child component at all. In fact is a good idea to avoid having state in most components so they are easy to reason and reuse.
I would leave all the state only on the parent component in this case.
TOP Component:
import React from "react";
import Search from "./search";
export default class Searches extends React.Component {
constructor(){
super();
this.state = {
searches : [
{
id : "2178348216",
searchName: "searchName1",
matches: "5"
},
{
id : "10293840132",
searchName: "searchName2",
matches: "20"
}
],
activeElement : null
};
}
_onSearchSelect(searchId) {
this.setState({'activeElement': searchId})
}
render() {
const { searches, activeSearchId } = this.state;
const SearchItems = searches.map((search) => {
return <Search key={search.id} {...search}
isActive={search.id === activeElement}
onSelect={this._onSearchSelect.bind(this)} />
})
return (
<div> {SearchItems} </div>
);
}
}
CHILD Component:
import React from "react";
export default class Search extends React.Component {
_getPanelClassNames() {
const { isActive } = this.props
return 'row panel panel-success ' + (isActive ? 'active' : 'default')
}
_onSelect() {
const { id, onSelect } = this.props;
onSelect(id)
}
render() {
const { searchName, matches } = this.props;
const panelStyle = { height: '90px', marginBottom: '6px', boxShadow: '' }
const buttonStyle = {
height: '100%',
width: '93px',
backgroundColor: '#FFC600'
}
return (
<div style={panelStyle} className={this._getPanelClassNames()}>
<div className="col-xs-4">
Search Name: {searchName}
</div>
<div className="col-xs-3">
Must Have: PHP, MySQL
</div>
<div className="col-xs-3">
Could Have: AngularJS
</div>
<div className="col-xs-2">
<button type="button" onClick={this._onSelect.bind(this)}
style={buttonStyle} className="btn btn-default btn-lg"
>
{matches}
</button>
</div>
</div>
);
}
}
You can also see it running in Plunker: https://plnkr.co/edit/sdWzFedsdFx4MpbOuPJD?p=preview
Ok it turns out this is simpler than I thought and is simply a case of understanding how react works(and not getting confused) .
When you have a top level component you pass it's state via props to children, when you update the state in the top level component it'll pass that down to the children and you can use componentWillReceiveProps to take action.
I added a function to my top level component called updateActiveSearch which simply sets the state of the TOP level component I then passed the activeElement state as a prop to the child Elements along with the function. When a child element calls this function to set itself as active all of them will fire componentWillReceiveProps, they simply just need to check their own ID against the one they've received, if it matches they're active, if it doesn't they're not!
So my top level component now looks like this:
export default class Searches extends React.Component {
constructor(){
super();
this.state = {
searches : [
{
id : "2178348216",
searchName: "searchName1",
matches: "5"
},
{
id : "10293840132",
searchName: "searchName2",
matches: "20"
}
],
activeElement : 0
};
}
// This function gets passed via a prop below
updateActiveSearch(id){
//console.log(id);
this.setState({activeElement : id});
}
render() {
const SearchItems = this.state.searches.map((search) => {
return <Search activeElement={this.state.activeElement} goFunction={this.updateActiveSearch.bind(this)} key={search.id} {...search}/>
})
return (
<div> {SearchItems} </div>
);
}
}
CHILD COMPONENTS
export default class Search extends React.Component {
constructor() {
super();
// Set the default panel style
this.state = {
panelStyle: { height: '90px', marginBottom: '6px', boxShadow: '' },
selected: false
}
}
// This happens right before the props get updated!
componentWillReceiveProps(incomingProps){
if(incomingProps.activeElement == this.props.id){
this.setState({selected: true});
} else {
this.setState({selected: false});
}
}
isActive(){
return 'row panel panel-success ' + (this.state.selected ? 'active' : 'default');
}
viewNotifications(e){
//this.state.panelStyle.boxShadow = '-2px 3px 20px 5px rgba(255,198,0,1)';
this.setState({selected: true});
this.props.goFunction(this.props.id);
}
render() {
const { id, searchName, matches } = this.props;
const buttonStyle = {
height: '100%',
width: '93px',
backgroundColor: '#FFC600'
}
return (
<div style={this.state.panelStyle} className={this.isActive()}>
<div class="col-xs-10">
<div class="col-xs-7">
Search Name: {searchName}
</div>
<div class="col-xs-7">
Must Have: PHP, MySQL
</div>
<div class="col-xs-7">
Could Have: AngularJS
</div>
</div>
<button type="button" onClick={this.viewNotifications.bind(this)} style={buttonStyle} class="btn btn-default btn-lg"> {matches} </button>
</div>
);
}
}

Categories

Resources