Increment / Decrement function in ReactJS - reactjs

I created a increment/decrement function, but I have a doubt.
I can decrement count clicking in same button that include a increment count?
How to create that function?
Code:
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
clicks: 0,
show: true
};
}
IncrementItem = () => {
this.setState({ clicks: this.state.clicks + 1 });
}
DecreaseItem = () => {
this.setState({ clicks: this.state.clicks - 1 });
}
ToggleClick = () => {
this.setState({ show: !this.state.show });
}
render() {
return (
<div>
<button onClick={this.IncrementItem}>Click to increment by 1</button>
<button onClick={this.DecreaseItem}>Click to decrease by 1</button>
<button onClick={this.ToggleClick}>
{ this.state.show ? 'Hide number' : 'Show number' }
</button>
{ this.state.show ? <h2>{ this.state.clicks }</h2> : '' }
</div>
);
}
}
export default App;

You could set a conditional in the function you trigger when you click on the button. Maybe something like this:
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0,
clicked: false,
};
}
toggleClicked = () => {
const counter = this.state.clicked ? counter +1 : counter - 1;
const clicked = !this.state.clicked;
this.setState({ counter, clicked })
}
render() {
return (
<div>
<button onClick={this.toggleClicked}>Click</button>
{ this.state.counter ? <h2>{ this.state.counter }</h2> : '' }
</div>
);
}
}
export default App;
This way if you have already clicked the counter will decrease by 1 and viceversa.

Related

How to make a step control component?

I have a component:
import React from "react";
import styles from "./Clicker.module.scss";
class Clicker extends React.Component {
constructor(props) {
super(props);
this.state = {
counter: props.counter,
step: props.step,
};
}
componentDidMount() {
this.setState({
counter: 0,
step: 1,
});
}
add = () => {
this.setState((previous) => ({
counter: previous.counter + previous.step,
}));
};
changeCountInput = ({ target: { value } }) => {
const toNumber = Number(value);
this.setState(toNumber > 0 ? { step: toNumber } : { step: 1 });
};
render() {
const { counter } = this.state;
return (
<article className={styles.container}>
<h1 className={styles.counter}>{counter}</h1>
<input
onChange={this.changeCountInput}
placeholder="set step (default - 1)"
className={styles.inputCount}
/>
<button onClick={this.add} className={styles.buttonCounter}>
Change counter
</button>
</article>
);
}
}
export default Clicker;
I need to make a step control component.
I don't understand how to do it.
I ask you to explain how to do this. I'm not asking you to do it for me, just theoretically, what should I do to create a step control component?
UPD:
As the teacher said, you need to move this piece of code into a separate component.
<input
onChange={this.changeCountInput}
placeholder="set step (default - 1)"
className={styles.inputCount}
/>

condition componentWillUnmount() in reactjs

When clicking on button, quote should unmount using componentwillunmount() but I checked by logging to console, unmount not at all running.
import React from "react";
import "./App.css";
class App extends React.Component {
constructor() {
super();
this.state = {
quote: ""
};
}
handleAction = () => {
this._isRemoved = true;
console.log("Clicked");
};
componentDidMount() {
this._isRemoved = false;
if (!this._isRemoved) {
this.setState({ quote: "Something" });
console.log("Mounted");
}
}
componentWillUnmount() {
this._isRemoved = true;
console.log("Unmount");
}
render() {
return (
<div className="App">
<div>
<q>{this.state.quote}</q>
<br />
<button onClick={this.handleAction}>Click Here</button>
</div>
</div>
);
}
}
export default App;
The unmount it's called when you change the page or somthing that the whole component will disappear .
you can simply hide or show your element like this example :
import React from "react";
import "./App.css";
class App extends React.Component {
constructor() {
super();
this.state = {
quote: "",
_isRemoved :false
};
}
handleAction = () => {
this.setState({
_isRemoved : true
})
console.log("Clicked");
};
showAction = () => {
this.setState({
_isRemoved : false
})
console.log("Clicked");
};
componentDidMount() {
this.setState({ quote: "Something" });
console.log("Mounted");
}
componentWillUnmount() {
this._isRemoved = true;
console.log("Unmount");
}
render() {
return (
<div className="App">
<div>
{!this.state._isRemoved &&
<q>{this.state.quote}</q>
}
<br />
<button onClick={this.handleAction}>Hide</button>
<button onClick={this.showAction}>Show</button>
</div>
</div>
);
}
}
export default App;

Reset count function in counter app not setting count to 0

I am facing a problem in resetting the count to 0 in my counter app. It's not resetting the count to 0 for any counter and I have tried console.log each counter.count but it's showing undefined.
App.js:
import React from "react";
import "./App.css";
import Counter from "./components/Counter";
export class App extends React.Component {
state = {
counters: []
};
addCounter = () => {
this.setState({
counters: [...this.state.counters, Counter]
});
};
reset = () => {
const counters = this.state.counters.map(counter => {
counter.count = 0;
return counter;
});
// console.log(counters.count);
this.setState({ counters });
};
render() {
return (
<div className="App">
<button onClick={this.reset}>Reset</button>
<button onClick={this.addCounter}>Add Counter</button>
{this.state.counters.map((Counter, index) => (
<Counter key={index} />
))}
</div>
);
}
}
export default App;
Counter.js:
import React, { Component } from 'react'
export class Counter extends Component {
state={
count:0,
}
increment=()=>{
this.setState({ count: this.state.count + 1 });
}
decrement=()=>{
this.setState({ count: this.state.count - 1 });
}
render() {
return (
<div>
<span><button onClick={this.increment}>+</button></span>
<span>{this.state.count}</span>
<span><button onClick={this.decrement}>-</button></span>
</div>
)
}
}
export default Counter
Reset counter should reset the count to 0 for all the counters.
When you use
this.setState({
counters: [...this.state.counters, Counter]
});
you saved a React Class into counters. So when you map to reset to 0, you need New Class to get state.count like this:
reset = () => {
const counters = this.state.counters.map(counter => {
(new counter()).state.count = 0;
return counter;
});
for (let counter of counters) {
console.log((new counter()).state);
}
this.setState({ counters });
};
It's time to lift the state up.
Move all your increment and decrement and reset functions to parent, App.
Also, add a separate array count state to monitor current count for each counter.
// App
export class App extends React.Component {
state = {
counters: [],
count: [] // additional state to store count for each counter
};
addCounter = () => {
this.setState({
counters: [...this.state.counters, Counter],
count: [...this.state.count, 0]
});
};
reset = () => {
this.setState({
count: this.state.count.map(c => 0)
});
};
// lifted up from Counter
increment = index => {
this.setState({
count: this.state.count.map((c, i) => (i === index ? c + 1 : c))
});
};
// lifted up from Counter
decrement = index => {
this.setState({
count: this.state.count.map((c, i) => (i === index ? c - 1 : c))
});
};
render() {
return (
<div className="App">
<button onClick={this.addCounter}>Add Counter</button>
<button onClick={this.reset}>Reset</button>
{this.state.counters.map((Counter, index) => (
<Counter
key={index}
increment={() => this.increment(index)}
decrement={() => this.decrement(index)}
count={this.state.count[index]}
/>
))}
</div>
);
}
}
// Counter
export class Counter extends React.Component {
render() {
return (
<div>
<span>
<button onClick={this.props.increment}>+</button>
</span>
<span>{this.props.count}</span>
<span>
<button onClick={this.props.decrement}>-</button>
</span>
</div>
);
}
}
Demo

Passing value from Button to setState in array

I am trying to created boolean filters in an array.
If a button is "Active" (True) it should add the button name to the array ("selected").
If the button is "Inactive" (false), it should remove it from the array.
However, only some values end up in set state. I put it in a codepen:
https://codesandbox.io/s/wpxD35Oog
import React from 'react';
import { render } from 'react-dom';
import { x } from './data.js';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
movies: x.movies,
};
}
render() {
const uniqueGenres = []
.concat(
...this.state.movies.map(movies =>
movies.genres.map(genres => genres.name),
),
)
.filter((genre, i, _) => _.indexOf(genre) === i);
return (
<div>
<div>
{uniqueGenres.map(e => <Filter1 key={e} result={e} />)}
<br />
</div>
</div>
);
}
}
class Filter1 extends React.Component {
constructor(props) {
super(props);
this.state = {
active: true,
selected: '',
};
}
handleActive = e => {
// this.setState(previousState => ({
// selected: [...previousState.selected, e.target.value]
// }));
console.log('pre-setState', e.target.value);
const active = !this.state.active;
const selected = e.target.value;
this.setState({
active: active,
selected: selected,
});
console.log('status', this.state.active, this.state.selected);
};
render() {
return (
<span>
<button onClick={this.handleActive} value={this.props.result}>
{this.props.result} {}<b>
{this.state.active ? 'Active' : 'Inactive'}
</b>
</button>
</span>
);
}
}
Ideally you would let the parent component (in this case, App) take care of managing the active genres. I rewrote part of your code to demonstrate it: https://codesandbox.io/s/JZWp1RQED

React force componentDidMount

I have the following:
import React from 'react';
import axios from 'axios';
class FirstName extends React.Component {
constructor(props) {
super(props);
this.state = {
submitted: false
};
}
getName () {
var name = this.refs.firstName.value;
this.setState(function() {
this.props.action(name);
});
}
handleSubmit (e) {
e.preventDefault();
this.setState({ submitted: true }, function() {
this.props.actionID(2);
this.props.activeNav('color');
});
}
render () {
return (
<div>
<h2>tell us your first name</h2>
<form>
<input
type="text"
ref="firstName"
onChange={this.getName.bind(this)}
/>
<div className="buttons-wrapper">
<button href="#">back</button>
<button onClick={this.handleSubmit.bind(this)}>continue</button>
</div>
</form>
</div>
);
}
};
class PickColor extends React.Component {
backToPrevious (e) {
e.preventDefault();
this.props.actionID(1);
this.props.activeNav('name');
}
goToNext (e) {
e.preventDefault();
this.props.actionID(3);
this.props.activeNav('design');
this.props.displayIconsHolder(true);
}
getColorValue(event) {
this.props.color(event.target.getAttribute("data-color"));
}
render () {
var colors = ['red', 'purple', 'yellow', 'green', 'blue'],
colorsLink = [];
colors.forEach(el => {
colorsLink.push(<li
data-color={el}
key={el}
onClick={this.getColorValue.bind(this)}
ref={el}>
{el}
</li>
);
});
return (
<section>
<ul>
{colorsLink}
</ul>
<button onClick={this.backToPrevious.bind(this)}>back</button>
<button onClick={this.goToNext.bind(this)}>continue</button>
</section>
);
}
}
class ConfirmSingleIcon extends React.Component {
goBack () {
this.props.goBack();
}
confirmCaptionandIcon (event) {
var optionID = event.target.getAttribute("data-option-id"),
name = event.target.getAttribute("data-option-name");
this.props.setOptionID(optionID);
this.props.setIcon(1, name, optionID, false);
}
goNext () {
this.props.goNext();
}
render () {
console.log(this.props.currentState);
var options = [],
that = this;
this.props.iconOptionsList.forEach(function(el){
options.push(<li onClick={that.confirmCaptionandIcon.bind(that)} key={el.option} data-option-name={el.option} data-option-id={el.id}>{el.option}</li>);
});
return (
<div>
<h2>Choose your caption</h2>
<h3>
{this.props.selectedIcon}
</h3>
<ul>
{options}
</ul>
<button onClick={this.goBack.bind(this)} >back</button>
<button onClick={this.goNext.bind(this)} >confirm</button>
</div>
);
}
}
class ConfirmCaption extends React.Component {
handleClick () {
var currentState = this.props.currentState;
this.props.setIcon(currentState.icon_ID, currentState.selectedIcon, currentState.option_ID, true);
this.props.setIconVisiblity(true);
this.props.setIconListVisiblity(false);
}
render () {
console.log(this.props.currentState);
return (
<div>
<p onClick={this.handleClick.bind(this)}>confirm icon and caption</p>
</div>
);
}
}
class ChooseIcon extends React.Component {
constructor(props) {
super(props);
this.state = {
icons: [],
iconList: true,
confirmIcon: false,
confirmCaption: false,
selectedIconOptions: '',
icon_ID: '',
option_ID: '',
selectedIcon: ''
};
this.setOptionID = this.setOptionID.bind(this);
this.setIconVisiblity = this.setIconVisiblity.bind(this);
this.setIconListVisiblity = this.setIconListVisiblity.bind(this);
}
setOptionID (id) {
this.setState({ option_ID: id })
}
setIconVisiblity (onOff) {
this.setState({ confirmIcon: onOff })
}
setIconListVisiblity (onOff) {
this.setState({ iconList: onOff })
}
componentDidMount() {
var url = `http://local.tshirt.net/get-options`;
axios.get(url)
.then(res => {
this.setState({ icons:res.data.icons });
});
}
handleClick (event) {
var iconId = event.target.getAttribute("data-icon-id"),
that = this;
this.state.icons.forEach(function(el){
if(el.id == iconId){
that.setState(
{
confirmIcon: true,
iconList: false,
selectedIcon: el.name,
icon_ID: iconId,
selectedIconOptions: el.option
}
);
}
});
}
goBack () {
this.setState(
{
confirmIcon: false,
iconList: true
}
);
}
goNext () {
this.setState(
{
confirmIcon: false,
iconList: false,
confirmCaption: true
}
);
}
render () {
var icons = [];
this.state.icons.forEach(el => {
icons.push(<li data-icon-id={el.id} onClick={this.handleClick.bind(this)} key={el.name}>{el.name}</li>);
});
return (
<div>
{this.state.iconList ? <IconList icons={icons} /> : ''}
{this.state.confirmIcon ? <ConfirmSingleIcon goBack={this.goBack.bind(this)}
goNext={this.goNext.bind(this)}
setIcon={this.props.setIcon}
selectedIcon={this.state.selectedIcon}
iconOptionsList ={this.state.selectedIconOptions}
setOptionID={this.setOptionID}
currentState={this.state} /> : ''}
{this.state.confirmCaption ? <ConfirmCaption currentState={this.state}
setIcon={this.props.setIcon}
setIconVisiblity={this.setIconVisiblity}
setIconListVisiblity={this.setIconListVisiblity} /> : ''}
</div>
);
}
}
class IconList extends React.Component {
render () {
return (
<div>
<h2>Pick your icon</h2>
<ul>
{this.props.icons}
</ul>
</div>
);
}
}
class Forms extends React.Component {
render () {
var form;
switch(this.props.formID) {
case 1:
form = <FirstName action={this.props.action} actionID={this.props.switchComponent} activeNav={this.props.activeNav} />
break;
case 2:
form = <PickColor displayIconsHolder={this.props.seticonsHolder} color={this.props.colorVal} actionID={this.props.switchComponent} activeNav={this.props.activeNav} />
break;
case 3:
form = <ChooseIcon setIcon={this.props.setOptionA} />
break;
}
return (
<section>
{form}
</section>
);
}
}
export default Forms;
"ChooseIcon" is a component that will get used 3 times therefore everytime I get to it I need to bring its state back as if it was the first time.
Ideally I would need to make this ajax call everytime:
componentDidMount() {
var url = `http://local.tshirt.net/get-options`;
axios.get(url)
.then(res => {
this.setState({ icons:res.data.icons });
});
}
is there a way to manually call componentDidMount perhaps from a parent component?
React handles component lifecycle through key attribute. For example:
<ChooseIcon key={this.props.formID} setIcon={this.props.setOptionA} />
So every time your key (it can be anything you like, but unique) is changed component will unmount and mount again, with this you can easily control componentDidMount callback.
If you are using the ChooseIcon component 3 times inside the same parent component, I would suggest you to do the ajax in componentDidMount of the parent component like this (exaclty how you have in your example, in terms of code)
componentDidMount() {
var url = `http://local.tshirt.net/get-options`;
axios.get(url)
.then(res => {
this.setState({ icons:res.data.icons });
});
}
and then pass this data down to the ChooseIcon component
render() {
return (
//do your stuff
<ChooseIcon icons={this.state.icons}/>
)
}
after this you will only need to set the received props in your ChooseIconcomponent, for that you only need to change one line in it's constructor:
constructor(props) {
super(props);
this.state = {
icons: props.icons, // Changed here!
iconList: true,
confirmIcon: false,
confirmCaption: false,
selectedIconOptions: '',
icon_ID: '',
option_ID: '',
selectedIcon: ''
};
this.setOptionID = this.setOptionID.bind(this);
this.setIconVisiblity = this.setIconVisiblity.bind(this);
this.setIconListVisiblity = this.setIconListVisiblity.bind(this);
}
The parent component can use a ref to call the function directly.
However, trying to force this function feels like a smell. Perhaps lifting the state higher up the component tree would solve this problem. This way, the parent component will tell ChooseIcon what to show, and there will not be a need to call componentDidMount again. Also, I assume the Ajax call can also occur once.

Resources