identifying a component in react - javascript

I was following Mosh Hamedani's tutorial here for creating react-app.
I did exactly what he said.
I'm trying to pass an argument called product to a function which is called for onClick
However, I'm getting an error for which I didn't find much info.
Here's the code:
import React, { Component } from "react";
class Counter extends Component {
state = {
count: 0
};
formatCount() {
return this.state.count === 0 ? "Zero" : this.state.count;
}
handleIncrement = product => {
console.log(product);
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<React.Fragment>
<span className={this.formatSpan()}>{this.formatCount()}</span>
<button
onClick={() => this.handleIncrement(product)}
className="btn btn-primary"
>
Increment
</button>
</React.Fragment>
);
}
}
export default Counter;
Here's the error:
Failed to compile.
./src/components/counter.jsx Line 41: 'product' is not defined
no-undef
Search for the keywords to learn more about each error.

This happened to you because you did not give an input to your function. product is your input variable not input value. If for example ran a code like below, and check your browser console you see that it prints value of 1.
import React, { Component } from "react";
class Counter extends Component {
state = {
count: 0
};
formatCount() {
return this.state.count === 0 ? "Zero" : this.state.count;
}
handleIncrement = product => {
console.log(product);
this.setState({ count: this.state.count + 1 });
};
render() {
const product=1;
return (
<React.Fragment>
<span className={this.formatSpan()}>{this.formatCount()}</span>
<button
onClick={() => this.handleIncrement(product)}
className="btn btn-primary"
>
Increment
</button>
</React.Fragment>
);
}
}
export default Counter;
I hope it was helpful. Please vote me up if it worked for you:)

Related

Can't find the ID in react prop

It's my first day learning react and I'm stuck with an issue (I'm following Mosh's tutorial):
import React, { Component } from "react";
class Counter extends Component {
state = {
value: this.props.value,
};
handleIncrement = () => {
console.log("Click!");
this.setState({ value: this.state.value + 1 });
};
handleDecrement = () => {
console.log("Click!");
if (this.state.value !== 0) {
this.setState({ value: this.state.value - 1 });
}
};
render() {
return (
<div className="row align-items-center">
<div className="col">
<span className={this.getBadgeClasses()}>{this.formatCount()}</span>
</div>
<div className="col">
<button
onClick={() => this.handleIncrement({ id: 1 })}
className="btn btn-dark"
>
+
</button>
<button
onClick={() => this.handleDecrement({ id: 1 })}
className={this.isLessThanZero()}
>
-
</button>
</div>
<div className="col">
<button
onClick={() => this.props.onDelete(this.props.id)}
className="btn btn-danger m-2"
>
Delete
</button>
</div>
</div>
);
}
isLessThanZero() {
let classes = "btn btn-dark ";
classes += this.state.value === 0 ? "disabled" : "";
return classes;
}
getBadgeClasses() {
let classes = "badge m-2 badge-";
classes += this.state.value === 0 ? "warning" : "primary";
return classes;
}
formatCount() {
let { value } = this.state;
return value === 0 ? <h1>Zero</h1> : value;
}
}
export default Counter;
This is a counter component that just responds to the buttons. I'm including those in another component:
import React, { Component } from "react";
import Counter from "./counter";
class Counters extends Component {
state = {
counters: [
{ id: 1, value: 0 },
{ id: 2, value: 0 },
{ id: 3, value: 0 },
{ id: 4, value: 0 },
],
};
handleDelete = (counterId) => {
console.log("Event detected! Delete", counterId);
};
render() {
return (
<div className="cont">
{this.state.counters.map((counter) => (
<Counter
value={counter.value}
key={counter.id}
onDelete={this.handleDelete}
></Counter>
))}
</div>
);
}
}
export default Counters;
In the handleDelete function, when called, I'm getting undefined for the counterId. When I check in the ReactComponents Chrome extention, I see that there isn't any ID:
Why is this happening?
The problem is you are not passing the counter for this.handleDelete. You need to explicitly pass it.
<Counter
value={counter.value}
key={counter.id}
onDelete={() => this.handleDelete(counter.id)}
/>
In the above snippet, I am passing a new function to the Counter component, the function just calls this.handleDelete with the counter.id of the corresponding component.

react components rendering horizontally instead of vertically

I am trying to render an array of counters using the counter component dynamically however they get rendered horizontally and I need them rendered vertically.
I am following the react tutorial from codewithmosh.com.
I have followed all the instructions and gone over the code countless times. I would also like to note that the instructor is using and older version of react and bootstrap
counter.jsx
import React, { Component } from "react";
class Counter extends Component {
state = {
count: this.props.value
};
handleIncriment = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<React.Fragment>
<span className={this.getBadgeClasses()}>{this.formatCount()}</span>
<button
onClick={this.handleIncriment}
className="btn btn-secondary btn-sm"
>
Incriment
</button>
</React.Fragment>
);
}
getBadgeClasses() {
let classes = "badge m-2 badge-";
classes += this.state.count === 0 ? "warning" : "primary";
return classes;
}
formatCount() {
const { count } = this.state;
return count === 0 ? "zero" : count;
}
}
export default Counter;
Counters.jsx
import React, { Component } from "react";
import Counter from "./counter";
class Counters extends Component {
state = {
counters: [
{ id: 1, value: 0 },
{ id: 2, value: 0 },
{ id: 3, value: 0 },
{ id: 4, value: 0 }
]
};
render() {
return (
<div>
{this.state.counters.map(counter => (
<Counter key={counter.id} value={counter.value} selected={true} />
))}
</div>
);
}
}
export default Counters;
If you use a div instead of a Fragment inside Counter, your counters will be block elements, and will render vertically.

Could not add multiple counters in react

I want to make a counter app with increment, decrement and add counter button. But add counter function is not working. It's showing this error:
Parsing error: Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...?
i have already tried enclosing it in tags,still its not working.
import React,{Component} from 'react';
import './App.css';
export class App extends Component {
state={
count:0,
}
increment=()=>{
this.setState({ count: this.state.count + 1 });
}
decrement=()=>{
this.setState({ count: this.state.count - 1 });
}
addCounter=()=>{
<span><button onClick={this.increment}>+</button></span>
<span>{this.state.count}</span>
<span><button onClick={this.decrement}>-</button></span>
}
render() {
return (
<div className="App">
<button onClick={this.addCounter}>Add Counter</button>
</div>
)
}
}
export default App;
add counter function should add another counter just below the previous counter.
Basically extract a Counter component.
Then have your App maintain a list of Counter components.
// Counter Component
export class Counter extends React.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>
);
}
}
// App Component
export class App extends React.Component {
state = {
counters: [], // additional state for Counter components
}
addCounter = () => {
this.setState({
counters: [
...this.state.counters,
Counter
]
})
}
render() {
return (
<div className="App">
<button onClick={this.addCounter}>Add Counter</button>
{ this.state.counters.map((Counter, index) => (
<Counter key={index} />)
)}
</div>
)
}
}
Demo
You are just adding more spans and button but refering to the same counter.
state={
i : 0,
count:0,
}
var newCount="counter"+this.state.i;
this.setState({
i : this.state.i+1,
})
this.setState({
count: {
...this.state.count,
newCount: 0
}
});
So with this you add a new counter with a progresive autoincrement number.
I think it is what you want to do.
const { useState } = React;
function App(){
const [counter, setCounter] = useState([]);
function addCounter(){
setCounter(counter.concat({id: counter.length, count: 0}));
}
function increase(id){
setCounter(counter.map(el=>{
if(el.id === id){
el.count++;
}
return el;
}));
}
function decrease(id){
setCounter(counter.map(el=>{
if(el.id === id){
el.count--;
}
return el;
}));
}
return (
<div className="App">
<button onClick={addCounter}>Add Counter</button>
{
counter.map(el=>{
return(
<div key={el.id}>
<span>Counter #{el.id}</span>
<div>
<button onClick={()=>{increase(el.id)}}>+</button>
<span>{el.count}</span>
<button onClick={()=>{decrease(el.id)}}>-</button>
</div>
</div>
)
})
}
</div>
)
}
ReactDOM.render(
<App />, document.getElementById('root')
)
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>
Your "counter" needs to be a react component with its own state, what you have there will have each "counter" you add use the same component state from App. You also do not save the returned JSX to then render anywhere.
const Counter = () => {
const [count, setCount] = useState(0);
return (
<div> // <-- React components can return only a single node*
<span>
<button onClick={() => setCount(count + 1)}>+</button>
</span>
<span>{count}</span>
<span>
<button onClick={() => setCount(count - 1)}>-</button>
</span>
</div>
);
};
class App extends Component {
state = {
counters: []
};
addCounter = () => {
this.setState(prevState => ({
counters: [...prevState.counters, <Counter />]
}));
};
render() {
return (
<div>
<button onClick={this.addCounter}>Add Counter</button>
{this.state.counters}
</div>
);
}
}
* React render lifecycle function can render arrays.
You have to add another functional Component instead of adding JSX. DEMO
import React from 'react';
const counter = (props) => {
return (
<div>
<span><button onClick={() => props.increment(props.index)}>+</button></span>
<span>{props.count}</span>
<span><button onClick={() => props.decrement(props.index)}>-</button></span>
</div>
)
}
export default counter;
And your main App component
import React, {Component} from 'react';
import Counter from './Counter';
import './App.css';
export class App extends Component {
state={
counters: []
}
valueChanger = (index, inc) => {
this.setState((prevState) => {
const counters = prevState.counters.slice();
counters[index] += inc;
return {
counters: counters
}
});
}
increment=(index)=>{
this.valueChanger(index, 1);
}
decrement=(index)=>{
this.valueChanger(index, -1);
}
addCounter=()=>{
this.setState((prevState) => {
return { counters: [...prevState.counters, 0] }
});
}
render() {
let counterElems = this.state.counters.map((c, index) => {
return <Counter key={index} index={index} increment={this.increment} decrement={this.decrement} count={c} />
});
return (
<div className="App">
{counterElems}
<button onClick={this.addCounter}>Add Counter</button>
</div>
)
}
}
export default App;

Not able to see value of increment button

New to react and I am removing the local state in my counter component and will be relying on the props to receive the data that it needs. I believe this is called a controlled component. After I got rid of the state and changed every where I was using this.state to this.props, I am no longer able to see the box that displays the value when I click my increment button. I will post all the code down below.
/* Counter Component*/
import React, { Component } from "react";
class Counter extends Component {
renderTags() {
return (
<ul>
{this.state.tags.length === 0 && <p> There are no tags </p>}
{this.state.tags.map(tag => (
<li key={tag}> {tag} </li>
))}
</ul>
);
}
// You can do styles this way or do it inline
// styles = {
// fontSize: 50,
// fontWeight: "bold"
// };
render() {
return (
<div>
<span style={{ fontSize: 20 }} className={this.getBadgeClasses()}>
{this.formatCount()}
</span>
<button
onClick={() => this.props.onIncrement(this.props.counter)}
className="btn btn-secondary btn-sm"
>
Increment
</button>
<button
onClick={() => this.props.onDelete(this.props.counter.id)}
className="btn btn-danger btn-sm m-2"
>
Delete
</button>
{/* {this.renderTags()}
<p>{this.state.tags.length === 0 && "Please create a new tag"}</p> */}
</div>
);
}
getBadgeClasses() {
let classes = "badge m-2 badge-";
classes += this.props.counter.value === 0 ? "warning" : "primary";
return classes;
}
formatCount() {
const { count } = this.props.counter;
return count === 0 ? "Zero" : count;
}
}
export default Counter;
/* Counters Component */
import React, { Component } from "react";
import Counter from "./counter";
class Counters extends Component {
state = {
counters: [
{ id: 1, value: 5 },
{ id: 2, value: 0 },
{ id: 3, value: 0 },
{ id: 4, value: 0 }
]
};
handleIncrement = counter => {
console.log(counter);
};
handleReset = () => {
const counters = this.state.counters.map(c => {
c.value = 0;
return c;
});
this.setState({ counters });
};
handleDelete = counterID => {
const counters = this.state.counters.filter(c => c.id !==
counterID);
this.setState({ counters });
};
render() {
return (
<React.Fragment>
<button onClick={this.handleReset} className="btn btn-dark btn-sm m-2">
Reset
</button>
{this.state.counters.map(counter => (
<Counter
key={counter.id}
onDelete={this.handleDelete}
counter={counter}
onIncrement={this.handleIncrement}
/>
))}
</React.Fragment>
);
}
}
export default Counters;
You can't see the values since you are using a wrong key for your counter.
formatCount() {
const { count } = this.props.counter;
return count === 0 ? "Zero" : count;
}
There isn't any key named count in your counter. It is value. So, you should use it or you need to destruct it like this:
const { value: count } = this.props.counter
But, using the same name is more consistent I think. Also, your Counter component would be a stateless one since you don't need any state or lifecycle method there.
One extra change would be done to the handler methods like onClick for onIncrement. If you use an arrow function, that function will be recreated in every render. You can use an extra handler method. Here is the complete working example (simplified for a clear view).
class Counters extends React.Component {
state = {
counters: [
{ id: 1, value: 5 },
{ id: 2, value: 0 },
{ id: 3, value: 0 },
{ id: 4, value: 0 }
]
};
handleIncrement = counter => {
const { counters } = this.state;
const newCounters = counters.map( el => {
if( el.id !== counter.id ) { return el; }
return { ...counter, value: counter.value + 1 }
} )
this.setState({ counters: newCounters});
};
handleReset = () => {
const counters = this.state.counters.map(c => {
c.value = 0;
return c;
});
this.setState({ counters });
};
handleDelete = counter => {
const { id: counterID } = counter;
const counters = this.state.counters.filter(c => c.id !== counterID);
this.setState({ counters });
};
render() {
return (
<div>
<button onClick={this.handleReset} className="btn btn-dark btn-sm m-2">
Reset
</button>
{this.state.counters.map(counter => (
<Counter
key={counter.id}
onDelete={this.handleDelete}
counter={counter}
onIncrement={this.handleIncrement}
/>
))}
</div>
);
}
}
const Counter = props => {
const { counter, onIncrement, onDelete} = props;
function formatCount(){
const { value } = counter;
return value === 0 ? "Zero" : value;
}
function handleIncrement(){
onIncrement( counter );
}
function handleDelete(){
onDelete( counter );
}
return (
<div>
<span>
{formatCount()}
</span>
<button
onClick={handleIncrement}
>
Increment
</button>
<button
onClick={handleDelete}
>
Delete
</button>
</div>
);
}
ReactDOM.render(<Counters />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Abstracting out onClick actions

I am attempting to abstract out functionality of a simple app based in ReactJS, however the console is throwing up a few problems that I can't figure out.
It seems to revolve around my use of this.setState(...)
I have commented out the original functions in the main Class just for visibility.
Button Class (will be moved to new file)
class Button extends React.Component {
buttonAction(props) {
switch (this.props.type) {
case 'add':
this.setState((prevState) => ({
counter: prevState + this.props.label
}));
break;
case 'take':
this.setState((prevState) => ({
counter: prevState - this.props.label
}));
break;
case 'reset':
this.setState(() => ({
counter: 0
}));
}
}
render() {
return (
<button onClick={this.buttonAction(this.props.type)}>{this.props.label}</button>
)
}
}
App class
class App extends React.Component {
// initial state of counter
state = {
counter: 0
};
// // function to increment the counter
// incrementCounter = (increment) => {
// this.setState((prevState) => ({
// counter: prevState.counter + 1
// }));
// };
render() {
return (
<div>
<Button type={'add'} label={1}/>
{this.state.counter}
</div>
)
}
}
const root = document.getElementById('root');
ReactDOM.render(<App />, root);
Please see my code example here
You are not calling buttonAction with the correct this context. You need to bind it to your component. Try adding this to your Button component:
constructor(props) {
super(props);
this.buttonAction = this.buttonAction.bind(this);
}
Edit:
The way of passing the event handler buttonAction is incorrect. This should be the correct way of doing it:
<button onClick={this.buttonAction}>{this.props.label}</button>
I managed to solve by looking at an older piece of code I already had...
#Jules Dupont was on the right track in his first answer, moving functions into the App class.
However, I decided to refactor a few things and have ended up with the following:
// Class of button to click
class Button extends React.Component {
// function to handle click
handleClick = () => {
this.props.onClickFunction(this.props.increment, this.props.type)
};
// what to render to virtual DOM
render() {
return (
<button
onClick={this.handleClick}>
{this.props.state}{this.props.increment}
</button>
);
}
}
// Result output
const Result = (props) => {
return (
<div>{props.counter}</div>
);
};
// App class
class App extends React.Component {
// initial state of counter
state = {
counter: 0
};
operators = {
'+': function (a, b) { return a + b },
'-': function (a, b) { return a - b },
// ...
};
// function to increment the counter
incrementCounter = (increment, type) => {
console.log('hit')
this.setState((prevState) => ({
counter: this.operators[type](prevState.counter,increment)
}));
};
// what to render to virtual DOM
render() {
return (
<div>
{/* Buttons of different incremental value */}
<Button type={'+'} increment={1} onClickFunction={this.incrementCounter} />
<Button type={'-'} increment={1} onClickFunction={this.incrementCounter} />
{/* result that will be displayed */}
<Result counter={this.state.counter} />
</div>
)
}
}

Categories