this is my first post and I am a beginner with JS and react so go easy. My issue is that I am not getting a number type returned where I want it... it occurs in the CurrentNumber component. Instead it is an [object Object] reference. I am assuming there is something wrong with my types/prop validation. Code posted below.(p.s. I also tried to use the number prop type in the validation) no dice.
import React, {useState,} from "react";
import PropTypes from 'prop-types';
const CurrentNumber = props => (
<p>
{props.countnumber}
</p>
);
const Applet = () => {
// Declaring a state variable called "count"
const [count, setCount] = useState(0);
//########################## Utils & Functions #########################
const incrementCount = props => {
setCount(props.countnumber+=1)
}
const resetCount = () => {
setCount(0);
}
// ######################### View in Browser ####################
return (
<div className={"game"}>
<div className={"header"}>
This is a header.
</div>
<div className={"body"}>
<button onClick={incrementCount}
countnumber = {count}
>
Click me
</button>
<CurrentNumber
countnumber={count}
/>
</div>
<p>
<button onClick = {resetCount}
countnumber = {count}
>
Reset
</button>
</p>
</div>
);
}
///////////// PROP VALIDATIONS ////////////////////
CurrentNumber.propTypes = {
countnumber: PropTypes.string,
}
////////////////////////////////////////////////////////////////////////////
export default Applet;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
const incrementCount = props => {
setCount(props.countnumber+=1)
}
should be
const incrementCount = () => {
setCount(c => c+1)
}
or alternatively
const incrementCount = () => {
setCount(count+1)
}
Related
I have found this error while trying to build another React app. So I am only asking the main issue here in a demo app, I might not be able to change any rendering methods here since it is not the actual project.
Issue in simplified form -> I was building a app where two count will be shown and a + button will be there next to that count value. When the button is clicked the count should be increased by 1. Unfortunately when I try to click on the button the value is increasing only the first time. After that the value is not even changing. But when I am implementing the same using Class component its working as expected.
Functional Component
import React, { useState } from "react";
function Page(props) {
const [count, setCount] = useState(0);
const [content, setContent] = useState({
button: (value) => {
return <button onClick={() => handlePlus(value)}>+</button>;
},
});
function handlePlus(value) {
console.log("value=", value);
const data = count + 1;
setCount((count) => data);
}
return (
<div>
<span>Functional Component Count = {count}</span>
{content.button(10)} // 10 will be replaced with another variable
</div>
);
}
export default Page;
Class Component
import React, { Component } from "react";
class PageClass extends Component {
state = {
count: 0,
content: {
button: (value) => {
return (
<button onClick={() => this.handlePlus(value)}>+</button>
);
},
},
};
handlePlus = (value) => {
console.log("value=", value);
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div>
<span>Class Component Count = {this.state.count}</span>
{this.state.content.button(10)} // 10 will be replaced with another variable
</div>
);
}
}
export default PageClass;
App.js
import "./App.css";
import Page from "./components/Page";
import PageClass from "./components/PageClass";
function App() {
return (
<div className="App">
<Page />
<PageClass />
</div>
);
}
export default App;
However, If I replace that content state variable with normal const variable type and it is working as expected.
Below is working when I am not using any hooks to render the button.
But this is not helpful for my case.
const content = {
content: () => {
console.log(count);
return <button onClick={() => handlePlus(value)}>+</button>;
},
};
I was trying to create some re-usable components and hence I wanted to have that function in state variable which return button tag, so that I can implements some other logic there.
The value will be missing since you're passing a hard-coded 10.
I'd recommend simplifying the handlePlus to just:
setCount(c => c + 1);
Then set the onclick like so:
<button onClick={handlePlus}>+</button>
And your code will work as expected as you can see in this snippet:
const { useState } = React;
const Example = () => {
const [count, setCount] = useState(0);
const [content, setContent] = useState({
content: (value) => {
return <button onClick={handlePlus}>+</button>;
},
});
function handlePlus(value) {
setCount(c => c + 1);
}
return (
<div>
<span>{count}</span>
{content.content(10)}
</div>
);
}
ReactDOM.render(<Example />, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>
That said, I'd recommend removing the button from the hook, and just render it yourself:
const { useState } = React;
const Example = () => {
const [count, setCount] = useState(0);
function handlePlus(value) {
setCount(c => c + 1);
}
return (
<div>
<span>{count}</span>
<button onClick={handlePlus}>+</button>
</div>
);
}
ReactDOM.render(<Example />, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>
See React documentation about the c => c + 1 syntax
Why variable a is undefined?
export default function Surah() {
let a;
const toDo= (id) => {
a = id;
console.log(a);
};
return (
<div>
<div>
<button onClick={() => toDo(1)}></button>
<div>{a}</div>
</div>
</div>
)
}
Can you explain what is wrong with my code ?
this is not an optimal way of using the variables. Instead we should useState hook. I'm attaching the code snippet below for your reference.
import {useState} from "react";
export default function Surah() {
const [a,setA] = useState(0); // Initialize it with a number
const Tafsir = (id) => {
setA(id);
};
return (
<div>
<div>
<button onClick={() => {
Tafsir(1);
console.log(a);
}}>Click Here</button>
<div>{a}</div>
</div>
</div>
)
}
you need to use a as state variable.
changing the variable value doesn't trigger the component to render again with the updated value, for this updated value you need to render the component again using setState.
import {useState} from "react";
export default function Surah() {
const [a, setA] = useState();
const toDo= (id) => {
setA(id);
};
return (
<div>
<div>
<button onClick={() => toDo(1)}>Click here</button>
<div>{a}</div>
</div>
</div>
)
}
There are two components, I want to implement an element array using the useContext hook, but when the button is clicked, the element is not removed, but on the contrary, there are more of them. Tell me what is wrong here. I would be very grateful!
First component:
import React from 'react';
import CartItem from './CartItem';
import Context from '../Context';
function Cart() {
let sum = 0;
let arrPrice = [];
let [products, setProducts] = React.useState([]);
let loacalProsucts = JSON.parse(localStorage.getItem('products'));
if(loacalProsucts === null) {
return(
<div className="EmptyCart">
<h1>Cart is empty</h1>
</div>
)
} else {
{loacalProsucts.map(item => products.push(item))}
{loacalProsucts.map(item => arrPrice.push(JSON.parse(item.total)))}
}
for(let i in arrPrice) {
sum += arrPrice[i];
}
function removeItem(id) {
setProducts(
products.filter(item => item.id !== id)
)
}
return(
<Context.Provider value={{removeItem}}>
<div className="Cart">
<h1>Your purchases:</h1>
<CartItem products = {products} />
<h1>Total: {sum}$</h1>
</div>
</Context.Provider>
)
}
Second component:
import React, { useContext } from 'react';
import Context from '../Context';
function CartList({products}) {
const {removeItem} = useContext(Context);
return(
<div className="CartList">
<img src={products.image} />
<h2>{products.name}</h2>
<h3 className="CartInfo">{products.kg}kg.</h3>
<h2 className="CartInfo">{products.total}$</h2>
<button className="CartInfo" onClick={() => removeItem(products.id)}>×</button>
</div>
);
}
export default CartList;
Component with a context:
import React from 'react';
const Context = React.createContext();
export default Context;
Adding to the comment above ^^
It's almost always a mistake to have initialization expressions inside your render loop (ie, outside of hooks). You'll also want to avoid mutating your local state, that's why useState returns a setter.
Totally untested:
function Cart() {
let [sum, setSum] = React.useState();
const loacalProsucts = useMemo(() => JSON.parse(localStorage.getItem('products')));
// Init products with local products if they exist
let [products, setProducts] = React.useState(loacalProsucts || []);
useEffect(() => {
// This is actually derived state so the whole thing
// could be replaced with
// const sum = products.reduce((a, c) => a + c?.total, 0);
setSum(products.reduce((a, c) => a + c?.total, 0));
}, [products]);
function removeItem(id) {
setProducts(
products.filter(item => item.id !== id)
)
}
...
I am trying to create dynamically element on button click and append it to one of the classes by using ref.
I can use document.createElement but from what I read do not use it in react
For example I want to add an element of <p> to div with class name of classes.elements by clicking the button
import React, { useRef } from 'react'
import classes from './AddElement.scss'
const AddElement = (props) => {
const elementRef = useRef(null)
const addElement = () => {
<p>This is paragraph</p>
}
return (
<div>
<button onClick={() => addElement()}>Click here</button>
<div ref={elementRef} className={classes.elements}>
</div>
</div>
)
}
export default AddElement;
You could try using the useState hook like this :
import React, { useState } from 'react';
import classes from './AddElement.scss';
const AddElement = () => {
const [dynamicElems, setDynamicElems] = useState([]);
const addElement = () => {
// Creates the dynamic paragraph
const newDynamicElem = <p className={classes.elements}>This is paragraph</p>;
// adds it to the state
setDynamicElems(() => [...dynamicElems, newDynamicElem]);
};
return (
<div>
<button onClick={() => addElement()}>Click here</button>
<div className={classes.elements}>{dynamicElems}</div>
</div>
);
};
export default AddElement;
const AddElement = (props) => {
const [dynamicCompList, setDynamicCompList] = useState([]);
const addElement = () => {
const dynamicEl = React.createElement("p", {}, "This is paragraph");
setDynamicCompList(dynamicCompList.concat(dynamicEl));
}
return (
<div>
<button onClick={() => addElement()}>Click here</button>
<div className={classes.elements}>
{dynamicCompList}
</div>
</div>
)
}
export default AddElement;
Try this:
const addElement = () => {
const para = document.createElement("p");
para.innerHTML = 'Hello';
elementRef.current.appendChild(para);
};
<div ref={elementRef}></div>
<button onClick={addElement}>Click me</button>
Approach 1
import React, { useRef, useState } from "react";
import classes from "./App.module.scss";
export default function App() {
const componetRef = useRef(null);
const [contentValue, setContentValue] = useState([]);
const addElement = () => {
const content = "this is para";
const type = componetRef.current.dataset.type || "p";
const classNames = componetRef.current.className;
const elemnt = React.createElement(type, { key: Date.now() }, content);
setContentValue([
...contentValue, // If you dont want to make it multiple times. just remove it
elemnt
]);
};
return (
<div className="App">
<button onClick={addElement}>Click here</button>
<div data-type="h1" ref={componetRef} className={classes.tag1}>
{contentValue}
</div>
</div>
);
}
Approach 2
import React, { useState } from "react";
import classes from "./App.module.scss";
const NewComponent = ({ classNames, content }) => {
return (
<div className={classNames} dangerouslySetInnerHTML={{ __html: content }} />
);
};
export default function App() {
// const classRef = useRef(null);
const [multiple, setMultiple] = useState([]);
const addElement = (e) => {
const classNames = e.target.dataset.class;
const content = e.target.dataset.content;
setMultiple([
...multiple, // If you dont want to make it multiple times. just remove it
<NewComponent
key={Date.now()}
classNames={classNames}
content={content}
/>
]);
};
return (
<div className="App">
<button
onClick={addElement}
data-class={classes.tag1}
data-content={"<p>asdasd</p>"}
>
Click here
</button>
{multiple}
</div>
);
}
Let me know if you have more question.
Here is sandbox
I am trying to change/toggle background of a button using inline-styling in React but getting error (in functional component).
Snippet:
import React, { useState } from 'react';
const Home2 = () => {
const [status, setStatus] = useState(false);
btnClick = () => {
setStatus(!status);
}
return (
<div><button onClick={btnClick} style={{background: status? 'orange': 'pink'}}>click 2</button></div>
)
}
export default Home2;
Error:
Kindly help.
You just need to define your btnClick with const.
const btnClick = () => {
setStatus(!status);
}
you need to change a bit your code:
import React, { useState } from 'react';
const Home2 = () => {
const [status, setStatus] = useState(false);
const btnClick = () => {
setStatus(!status);
}
return (
<div>
<button
onClick={btnClick}
style={{background: status? 'orange': 'pink'}}
>
click 2
</button>
</div>
)
}
export default Home2;
You missed const in the function declaration.
You forgot const:
const Home2 = () => {
const [status, setStatus] = React.useState(false);
const btnClick = () => {//you forgot const here
setStatus(status=>!status);//just to be save, use a callback
}
return (
<div><button onClick={btnClick} style={{background: status? 'orange': 'pink'}}>click 2</button></div>
)
}
ReactDOM.render(<Home2 />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
You need to explicitly define the callback function. In classes, we reference callbacks with this. In hooks, this is omitted (luckily) so you have to define a function explicitly.
Because you have not defined the btnClick function. The way it is declared is incorrect. Below should work.
const Home2 = () => {
const [status, setStatus] = useState(false);
const btnClick = () => {
setStatus(!status);
}
return (
<div><button onClick={btnClick} style={{background: status? 'orange': 'pink'}}>click 2</button></div>
)
}
export default Home2;
The way you have declared btnClick is a syntax we follow in class based components. In functional component, you need let or const to declare variables/functions.