onClick event doesn't act in ReactJS - javascript

I have a react code where I have onClicke event. I suppose to get implementation of function(someFunction). I didn't get any error running this code, everything else works. I guess the problem can be in function. The React code is
class Hello extends Component {
constructor() {
super();
this.num = { number: 4 };
this.someFunction = this.someFunction.bind(this);
}
someFunction() { this.setState({ number: this.num.number + 3 }); }
render() {
const coco = {
color: 'blue',
background: 'yellow',
width: '200px',
height: '200px',
padding: 'lem'
};
return (<div style={coco} onClick={this.someFunction}>
<p style={coco} onClick={this.someFunction}> bly blya
Hello {this.props.name} </p>
<p style={coco} onClick={this.someFunction} >
Current count: {this.num.number + 3}
</p>
</div>)
}
}
render(<Hello/>, document.getElementById('container'));

actually it is working just fine , your component isn't updating because it doesn't depend on state in fact you havne't defined any state in the constructor which might be a typo ..
import React , {Component} from 'react'
import ReactDOM from 'react-dom'
class Hello extends Component {
constructor() {
super();
// defining state
this.state = { number: 4 };
this.someFunction = this.someFunction.bind(this);
}
someFunction() {
//chnaging state case re-render for component
this.setState({number: this.state.number + 3 });
}
render() {
const coco = {
color: 'blue',
background: 'yellow',
width: '200px',
height: '200px',
padding: 'lem'
};
return (
<div style={coco} onClick={this.someFunction}>
<p style={coco} onClick={this.someFunction}> bly blya
Hello {this.props.name} </p>
<p style={coco} onClick={this.someFunction} >
Current count: {this.state.number + 3 /*need to use state here . */}
</p>
</div>
)
}
}
ReactDOM.render(<Hello/>, document.getElementById('container'));

You should replace:
Current count: {this.num.number + 3}
with:
Current count: {this.state.num.number + 3}

Instead of defining this.num, you should define the initial state of your component in the constructor:
this.state = {
number: 4,
};
Your function gets correctly called on the click callback, however the logic of updating the state doesn't work because it always returns the same state. this.num.number always has a value of 4 and thus your state will always have a value of 7 after calling setState.
You can use the previous state to calculate the new state like this:
this.setState((prevState) => {
return {
number: prevState.number + 3
};
});
See this JSFiddle

Related

React State Array Altered After Deployment

My dice roll app rolls the dice and logs the previous rolls.
You can see the app on github pages: https://mtrussell.github.io/dice-roll-app/
To see all the app's code, here is the repo: https://github.com/mtrussell/dice-roll-app
The RollDice component adds the current roll to the rolls array. The PrevRolls component reverses the array and maps it to the jsx elements.
The problem is that when I deployed it to github pages it behaves differently than on my local machine. It seems to alter the array after a second, flipping it back around.
I thought that it had something to do with the button timeout, but I removed that completely and the wonky behavior persisted.
I have tried restructuring my code in a number of different ways, but nothing seems to fix it.
RollDice Component -
import React, { Component } from 'react';
import Die from './Die';
import PrevRolls from './PrevRolls';
import './RollDice.css';
class RollDice extends Component {
static defaultProps = {
sides: ['one', 'two', 'three', 'four', 'five', 'six']
}
constructor(props) {
super(props);
this.state = {
die1: 'one',
die2: 'two',
rolling: false,
rolls: []
}
this.handleClick = this.handleClick.bind(this);
}
randomDice() {
const newDie1 = this.props.sides[
Math.floor(Math.random() * this.props.sides.length)
];
const newDie2 = this.props.sides[
Math.floor(Math.random() * this.props.sides.length)
];
return [newDie1, newDie2];
}
roll(dice) {
this.setState(prevState => {
return {
die1: dice[0],
die2: dice[1],
rolling: true,
rolls: [...prevState.rolls, {first: dice[0], second: dice[1]}]
}
});
}
handleClick() {
const dice = this.randomDice();
this.roll(dice);
setTimeout(() => {
this.setState({
rolling: false,
});
}, 1000);
}
render() {
let rollButton = this.state.rolling ? 'Rolling...' : 'Roll Dice!';
return(
<div className='RollDice'>
<div className='RollDice-dice'>
<Die face={this.state.die1} rolling={this.state.rolling} />
<Die face={this.state.die2} rolling={this.state.rolling} />
</div>
<button onClick={this.handleClick} disabled={this.state.rolling}>{rollButton}</button>
<PrevRolls rolls={this.state.rolls} />
</div>
);
}
}
export default RollDice;
PrevRolls Component -
import React, { Component } from 'react';
import './PrevRolls.css'
class PrevRolls extends Component {
constructor() {
super();
this.displayRolls = this.displayRolls.bind(this);
}
reverseRolls() {
return this.props.rolls.reverse();
}
displayRolls() {
return this.reverseRolls().map((roll, index) => (
<p>
Roll {this.props.rolls.length - index} <i className={`fas fa-dice-${roll.first}`} ></i> <i className={`fas fa-dice-${roll.second}`} ></i>
</p>
));
}
render() {
return(
<div className='PrevRolls'>
<div className='PrevRolls-list'>
{this.displayRolls()}
</div>
</div>
);
}
}
export default PrevRolls;
Thanks to xadm, I got this figured out. reverse() was altering the parent component's array.
I changed the way I was setting the state in the roll() function in the RollDice component.
In the PrevRolls component I removed the reverseRolls() function and its function call in the displayRolls() function.
RollDice Component -
roll(dice) {
this.setState(prevState => {
return {
die1: dice[0],
die2: dice[1],
rolling: true,
rolls: [{first: dice[0], second: dice[1]}, ...prevState.rolls]
}
});
}
PrevRolls Component -
displayRolls() {
return this.props.rolls.map((roll, index) => (
<p>
Roll {this.props.rolls.length - index} <i className={`fas fa-dice-${roll.first}`} ></i> <i className={`fas fa-dice-${roll.second}`} ></i>
</p>
));
}

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?

Custom component not getting rendered properly on this basic ReactJS app

I have a very basic ReactJS app which uses Redux which contains the following components:
PanelMaterialSize > Select
/src/controls/PanelMaterialSize/PanelMaterialSize.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import './PanelMaterialSize.scss';
import Select from '../Select/Select';
import { setThemeList } from '../../store/AppConfig/actions';
class PanelMaterialSize extends Component {
componentDidMount() {
this.n = 1;
setInterval(() => {
let themeList = [
{ value: this.n, text: 'Option ' + this.n },
{ value: this.n + 1, text: 'Option ' + (this.n + 1) },
{ value: this.n + 2, text: 'Option ' + (this.n + 2) },
];
this.props.setThemeList(themeList);
this.n += 3;
}, 1000);
}
render() {
return (
<div className="partial-designer-panel-material-size">
<div>
<div className="label-input">
<div className="label">MATERIAL</div>
<div className="input">
<Select data={this.props.themeList} style={{ width: '100%' }} />
</div>
</div>
</div>
</div>
);
}
}
const mapStateToProps = (appState) => {
return {
themeList: appState.appConfig.themeList,
}
}
const mapDispatchToProps = (dispatch) => {
return {
setThemeList: (themeList) => dispatch(setThemeList(themeList)),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(PanelMaterialSize);
In my opinion the Redux logic is fine because I have tested by doing couple of things.
My problem is that when the render(...) method of: PanelMaterialSize gets called, the component: Select doesn't get rendered with the new data (which changes every one second).
Here you have a Codesandbox.io you can play with (preferable use Chrome):
https://codesandbox.io/s/03mj405zzv
Any idea on how to get its content changed properly?
If possible, please, provide back a new Codesandbox.io with your solution, forked from the previous one.
Thanks!
the problem is here in your select component.
you are passing initially empty array and checking your component with this.state.data props, next time reducer change your this.state.data will not update the data. because you initialize in constructor. constructor only invoke once when component mount.
SOLVED DEMO LINK
The Problem is in your select render method:
render() {
let data = this.state[this.name];
return (
<div className="control-select" {...this.controlProps}>
<div className="custom-dropdown custom-dropdown--grey">
<select className="custom-dropdown__select custom-dropdown__select--grey">
//change data with this.props.data
{this.props.data.length > 0 &&
this.props.data.map((elem, index) => {
return (
<option value={elem.value} key={index}>
{elem.text}
</option>
);
})}
</select>
</div>
</div>
);
}

React: How to display a default object and highlight li bold?

I am fairly new to React and was wondering if anybody could give me an insight on a problem I am stuck with.
Right now I have a parent(Hello.js) component and two children(Mixer.js and renderCont.js) at the same level.
I am trying to render a list in the Mixer.js and display its corresponding objects in the Hello.js through by passing the values into RenderCont.js. I've gotten to a point where nothing is displayed before I click on any of the list to pass on a object.
From here is where I am stuck: I want the first object of the list to be displayed as a default, at the same time bold the first in the list. And then execute the as I have below.
This is my first time posting a question on stackoverflow so I'm not sure if my question makes sense with the attached codes but I will greatly appreciate any kind of support.
Parent Hello.js:
import React, { Component } from 'react';
import RenderCont from './renderCont.js';
import Mixer from './Mixer';
class Hello extends Component{
constructor(props) {
super(props);
this.state = {
items: [{
id: 0,
name: "First",
background: "white"
}, {
id: 1,
name: "Second",
background: "yellow"
}, {
id: 2,
name: "Third",
background: "blue"
}],
selectedItem: 0
}
this.handle = this.handle.bind(this)
}
handle(value) {
// console.log(this.state.selectedItem);
this.setState({
selectedItem: value
})
}
render() {
const list = this.state.items.map((item) => {
return(item);
})
return (
<div>
<Mixer item={list} onClick={this.handle} selected={this.state.selectedItem}/>
<ul id = "todo" >
<RenderCont item={this.state.selectedItem}/>
</ul>
</div>
)
}
}
export default Hello;
Mixer.js Child1:
import React, { Component } from 'react';
class Mixer extends Component{
constructor(props) {
super(props);
this.state = {
}
this.handleClick = this.handleClick.bind(this);
}
handleClick(item){
this.props.onClick(item);
}
renderTodos(propItems) {
return (
<div>
{propItems.map((item) => (
<li className={this.props.selected === item ? 'media clicked' : 'media'}
key={item.id} onClick = {() => this.handleClick(item)}>
{item.name}
</li>
))}
</div>
)
}
render() {
return (
<div className="yoyoyo">
{this.renderTodos(this.props.item)}
</div>
)
}
}
export default Mixer;
Second Child Comp renderCont.js :
import React, { Component } from 'react';
class RenderCont extends Component{
constructor(props) {
super(props);
}
renderBox(item){
return(
<div style={{color:item.background}}>
{item.id}
{item.name}
</div>
)
}
render() {
return (
<div className="yoyo">
{this.renderBox(this.props.item)}
</div>
)
}
}
export default RenderCont;
and the CSS:
.yoyo{
left: 500px;
background-color:red;
width:500px;
height:500px;
}
.media{
color: black;
}
.clicked{
font-weight: 900;
}
.yoyoyo{
background-color:lightblue;
width:200px;
height:200px;
}
I think the problem is some mismatch between the initial and eventual value of this.props.selected in Mixer.js. You initially set this.state.selectedItem = 0, and this is what is initially passed as the selected prop to Mixer. But the test you apply in that component is
this.props.selected === item ?
While there is one item.id that === 0, there is never an item that === 0. So no items are highlighted at first. But then, once an item is clicked and selectedItem is actually set to an item, the entry is made bold.
So it looks like you need to either make your initial selection equal to the item.id === 0 reference, or consistently refer to items within your components by their id's.

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