When I click on bet now the function triggers a console.log from another component. betNow should group all the inputs from stake in one common array but when I click on it it renders the console log from stake and includes all the values that I typed into one array. Everything works but not as I wish. The parent component should display the common array with all the values. I do not understand why it is happening.Could anyone explain me why is reacting like that? Thanks in advance
Parent Component
import React, { useState } from 'react';
import Button from '#material-ui/core/Button';
import FilterMenu from "./selectButton";
import FetchRandomBet from "./fetchRandomBets";
function Betslip() {
const data = [
{
value: 0,
label: "No Filter"
},
{
value: 1,
label: "Less than two"
},
{
value: 2,
label: "More than two"
},
]
const [selectedValue, setSelectedValue] = useState(0);
const [allStakes, setAllStakes] = useState([]);
const handleChange = obj => {
setSelectedValue(obj.value);
}
const betNow = () => {
const stakes = localStorage.getItem("stakes");
const jsnStake = JSON.parse(stakes) || [];
setAllStakes([...allStakes, jsnStake]);
}
return (
<div className="betslip">
<div className="betslip-top">
<h1 className="text">BETSLIP</h1>
<p className="text-two">BET WITH US!</p>
<div>
<FilterMenu
optionsProp={data}
valueProp={selectedValue}
onChangeProp={handleChange}
/>
</div>
</div>
<div>
<FetchRandomBet
valueProp={selectedValue}
/>
</div>
<Button
onClick={betNow}
className="betnow"
variant="contained"
>
Bet Now!
</Button>
</div>
);
}
export default Betslip;
Child Component
import React, { useState, useEffect } from 'react';
function Stake() {
const [stakes, setStakes] = useState([]);
const addStake = (e) => {
e.preventDefault();
const newStake = e.target.stake.value;
setStakes([newStake]);
};
useEffect(() => {
const json = JSON.stringify(stakes);
localStorage.setItem("stakes", json);
}, [stakes]);
console.log(stakes)
return (
<div>
<form onSubmit={addStake}>
<input
style={{
marginLeft: "40px",
width: "50px"
}}
type="text"
name="stake"
required
/>
</form>
</div>
);
}
export default Stake;
You have this console.log in you function that will run every time the component is rendered, since it´s outside of any function:
Related
I am new to React and using React 18 in this app. My problem is that if I click one button inside a map function, it reflects information about all the items. I want only that item information to show for which I clicked the button. The isShown === true part in the CountryInfo.js file is what should reflect only one item; currently clicking the show button shows all item information on the UI (I don't want this to happen). How do I do this?
Visually, this is my UI,
If you see the image above, clicking any show button returns all countries information, which should not happen.
Below is my code:
App.js
import { useState, useEffect } from 'react';
import axios from "axios";
import CountryInfo from './components/CountryInfo';
const App = () => {
const [countries, setCountries] = useState([]);
const [searchCountry, setSearchCountry] = useState("");
const handleCountryChange = event => {
setSearchCountry(event.target.value);
}
const getAllCountriesData = () => {
axios.get("https://restcountries.com/v3.1/all")
.then(response => {
setCountries(response.data);
})
}
useEffect(() => {
getAllCountriesData();
}, []);
return (
<>
<h2>Data for countries</h2>
find countries:
<input value={searchCountry} onChange={handleCountryChange} />
{searchCountry.length > 0 && <CountryInfo countries={countries} searchCountry={searchCountry} />}
</>
)
}
export default App;
CountryInfo.js
import React from "react";
import { useState } from "react";
const CountryInfo = ({ countries, searchCountry }) => {
const [isShown, setIsShown] = useState(false);
let filteredList = countries.filter(country =>
country.name.common.toLowerCase().includes(searchCountry.toLowerCase()));
const handleClick = () => {
setIsShown(true);
}
if (filteredList.length > 10) {
return <div>Too many matches, specify another filter</div>
}
else {
return filteredList.map(country => {
return (
<>
<div key={country.name.common}>
{!isShown &&
<div>
{country.name.common}
<button type="submit" onClick={handleClick}>show</button>
</div>
}
{isShown &&
<div key={country.name.common}>
<h2>{country.name.common}</h2>
<p>
Capital: {country.capital}
{'\n'}
Area: {country.area}
</p>
Languages:
<ul>
{
Object.values(country.languages)
.map((language, index) => <li key={index}>{language}</li>)
}
</ul>
<img src={country.flags.png} alt={`${country.name.common} flag`} height={150} />
</div>
}
</div>
</>
)
})
}
}
export default CountryInfo;
The method closeBet does not seem to trigger anything when I click it. Could anyone explaining me why? I think I implemented everything properly just does not understand why is not working properly. Also even the other useState isActive is not doing its job. So what Should I do in order to change useState properly onClick.Thanks in advance
import React, { useState } from 'react';
import Button from '#material-ui/core/Button';
import FilterMenu from "./selectButton";
import FetchRandomBet from "./fetchRandomBets";
function Betslip() {
const data = [
{
value: 0,
label: "No Filter"
},
{
value: 1,
label: "Less than two"
},
{
value: 2,
label: "More than two"
},
]
const [selectedValue, setSelectedValue] = useState(0);
const [allStakes, setAllStakes] = useState(null);
const [isActive, setActive] = useState("false");
const handleChange = obj => {
setSelectedValue(obj.value);
}
const betNow = () => {
if (!allStakes) {
const stakes = localStorage.getItem("stakes");
const jsnStake = JSON.parse(stakes) || [];
setAllStakes([jsnStake]);
setActive(isActive);
console.log('yes')
} else if (allStakes) {
localStorage.setItem("stakes", null);
setAllStakes([])
console.log('no')
}
}
const closeBet = () => {
setActive("false");
}
console.log(allStakes)
return (
<div className="betslip">
<div className="betslip-top">
<h1 className="text">BETSLIP</h1>
<p className="text-two">BET WITH US!</p>
<div>
<FilterMenu
optionsProp={data}
valueProp={selectedValue}
onChangeProp={handleChange}
/>
</div>
</div>
<div>
<FetchRandomBet
valueProp={selectedValue}
/>
</div>
<Button
onClick={betNow}
className="betnow"
variant="contained"
>
Bet Now!
</Button>
<div className={isActive ? "bet-show" : "bet-noshow"}>
<button
onClick={closeBet}>
x
</button>
<h1>
{allStakes}
</h1>
</div>
</div >
);
}
export default Betslip;
You are using string instead of boolean value for your isActive state. It should be like this:
const [isActive, setActive] = useState(false);
and when updating the state:
const closeBet = () => {
setActive(false);
}
Remove quotes around false value and it should work as expected.
You probably want to set the value to false, so you should not use quotes as it becomes a string then.
setActive(false);
i have 3 components: Form (parent), Picklist and ParagraphBox (children); based on the select of the picklist, i render ParagraphBox and also a "+" button. What i would like to achieve is on the click of the plus button, render another ParagraphBox, just under the first. I would also like the remove functionality.
My ParagraphBox component has a title and a content, and i want to give the adding a progressive number:
e.g Paragraph 1
Content: ....
Paragraph 2
Content: ....
And so on
Here's my ParagraphBox component:
import React, { useState, useEffect } from 'react';
export default function ParagraphBox(props) {
const [paragrafo, setParagrafo] = useState({})
useEffect(() => {
console.log('paragrafo ', paragrafo)
props.onChange(paragrafo)
}, [paragrafo])
const onChange = (e) => {
const titolo = e.target.name
const contenuto = e.target.value
setParagrafo({
...paragrafo,
[titolo]: contenuto
})
}
return (
<div className = "paragraph-box">
<label>
{props.labelInputBox}
<div>
<input type="text" name="titolo" value={paragrafo.titolo || ''} onChange={onChange}/>
</div>
{props.labelTextArea}
<div>
<textarea id="subject" name="contenuto" placeholder="Inserisci contenuto.." style={{height: "45x", width: "400px"}} value={paragrafo.contenuto || ''} onChange={onChange} />
</div>
</label>
</div>
)
}
Here is my Form component:
import React, { useState, useEffect, useRef } from 'react';
import './Form.css'
import createDocument from '../pdfTool';
import finalita from '../icons/finalita.PNG';
import Picklist from './Picklist.js';
import ParagraphBox from './ParagraphBox';
export default function Form() {
const [flagImg, setFlagImg] = useState(false)
const [flagPar, setFlagPar] = useState(false)
const [paragArray, setParagArray] = useState([
{titolo: '', contenuto: ''}
])
const handleChange = (e) => {
console.log('e ', e)
console.log('e.titolo PARENT ', e.titolo)
console.log('e.contenuto PARENT ', e.contenuto)
setParagArray({
...paragArray,
[e.titolo]: e.contenuto
})
}
useEffect(() => {
console.log('rendering useEffect')
console.log('flagPar: ', flagPar)
console.log('flagImg: ', flagImg)
console.log('paragArray ', paragArray)
}, [flagPar, flagImg, paragArray])
const handleSubmit = (evt) => {
evt.preventDefault(); //usato per evitrare il refresh del browser
}
const addParag = (parag) => {
console.log('paragArray PARENT ', paragArray)
}
const onSelect = (selectedValue) => {
console.log('valore selezionato nella picklist: ' + selectedValue)
if(selectedValue === 'Layout 2') {
setFlagImg(true)
setFlagPar(true)
}
}
return(
<div>
<Picklist onSelect={onSelect} label="Seleziona un layout di contratto: " pickVals={["Seleziona...", "Layout 1", "Layout 2", "Layout 3"]}/>
{flagImg ? (
<form onSubmit={handleSubmit}>
<Picklist onSelect={onSelect} label="Seleziona Immagine: " pickVals={["Seleziona...", "Immagine 1", "Immagine 2", "Immagine 3"]} />
</form>
) : null}
{flagPar ? (
<div>
<ParagraphBox labelInputBox="Paragfrafo 1" labelTextArea="Contenuto Paragrafo" onChange={handleChange}/>
<div id = "add-paragraph">
<button type="button" onClick={addParag}>+</button>
<input type="submit" value="Submit" />
</div>
</div>
) : null}
</div>
)
Thanks in advance for your time
I know this is old...but I just faced the same issue, so here it goes: JSX is just syntactic sugar for regular JavaScript. Therefore you can just create the component manually and make it available as part of your hook, i.e.:
custom hook:
import React, { useState } from 'react';
import Advise from '../../components/Advise/Advise';
const useAdvise = () => {
const [ showAdvise, setShowAdvise ] = useState(false)
const [ adviseMsg, setAdviseMsg ] = useState('')
const [ loading, setLoading ] = useState(false)
const hideAdvise = () => {
setShowAdvise(false)
}
const adviseComponent = React.createElement(Advise, {show:showAdvise, showSpinner:loading, hideAdvise:hideAdvise, children:adviseMsg})
return {
adviseComponent,
setShowAdvise,
setAdviseMsg,
setLoading
}
};
export default useAdvise;
component where I want it:
import useAdvise from '../hooks/useAdvise/useAdvise'
const Page = () => {
const {adviseComponent, setShowAdvise, setAdviseMsg, setLoading} = useAdvise()
return(
<div>
{adviseComponent}
</div>
)
}
hope it helps (cheers from Br as well)
I have a button on my webpage, and I want an input tag to appear, whenever the user clicks that button. I earlier tried something like this:
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [showInput, setShowInput] = useState(false);
const handleClick = () => setShowInput(true);
return (
<div className="App">
<button onClick={handleClick}>Click me</button>
{showInput ? <input type="text" /> : ""}
</div>
);
}
But this only worked once. I want it to add an input tag whenever the user clicks that button. How do I do so?
Instead of maintaining the number of input elements in the state, i suggest that you maintain an object in the state that is initially empty. Once the button is clicked to add an input, you could update the object with a key-value pair that represents the new input element.
State after adding one input could like as shown below:
{
input1: { value: '' }
}
Similarly, as more inputs are added, more objects will be added in the state.
This will allow your input elements to be controlled components and will allow you to handle the onChange event with only one event handler function.
Demo
let counter = 1;
function App() {
const [inputs, setInputs] = React.useState({});
const handleClick = () => {
const inputName = "input" + counter++;
const inputObj = { value: "" };
setInputs({ ...inputs, [inputName]: inputObj });
};
const handleChange = (event) => {
const { name, value } = event.target;
setInputs({ ...inputs, [name]: { ...inputs[name], value } });
};
return (
<div className="App">
<button onClick={handleClick}>Add Input</button>
<div className="inputContainer">
{Object.keys(inputs).map((inputName) => {
const { value } = inputs[inputName];
return (
<input
key={inputName}
name={inputName}
value={value}
onChange={handleChange}
placeholder={inputName}
/>
);
})}
</div>
</div>
);
}
ReactDOM.render(<App/>, document.querySelector('#root'));
.App {
font-family: sans-serif;
text-align: center;
}
.inputContainer {
display: flex;
flex-direction: column;
max-width: 300px;
margin: 10px auto;
}
input {
margin: 5px;
padding: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Make showInput a number that defaults to 0.
Have handleClick increment that number instead of just setting true.
Outside the return expression, create an array. With a for loop, push inputs (until you reach the number specified) into the array.
Replace the line where you add the input to the JSX with that array.
Something like ...
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [inputs, setInputs] = useState([]);
const handleClick = () => setInputs([...inputs, ""]);
return (
<div className="App">
<button onClick={handleClick}>Click me</button>
{inputs.map(i => <input type="text"/>)}
</div>
);
}
Now you can also store your input values into your inputs state for further processing.
I leave formatting up to you ... !
import React, { useState } from "react";
export default function App() {
const initialValue = [{ value: "first input" }];
const [userInputs, setUserInputs] = useState(initialValue);
const handleClick = () => {
const updatedInputs = [...userInputs, { value: "new input"}]
setUserInputs(updatedInputs);
}
return (
<div className="App">
<button onClick={handleClick}>Click me</button>
{userInputs.map((el, i) => (
<input type="text" value={el.value} />
))}
</div>
);
}
All of the implementation above is correct, But I also have my own implementation.
import React, { useState, Fragment } from "react";
export default function App() {
const [showInputs, setInputs] = useState([]);
const handleClick = () => {
setInputs((prev) => {
const i = prev.length + 1;
return [
...prev,
<Fragment key={i}>
<input type="text" />
<br />
</Fragment>
];
});
};
return (
<div className="App">
<button onClick={handleClick}>Click me</button>
<br />
{showInputs}
</div>
);
}
Currently, the details of the elements which I want to display are saved at Info.js.
Parent.js is responsible for importing the details needed and then injecting them respectively into each Child.js by .map function as the info stored at Info.js is an array.
I want to dynamically display the relative Child component by the button pressed by users. For example, when the user clicked "First-Tier" button at Parent.js, only the Child.js component with the category of "First-Tier" will be shown. At this moment, my code is not working. I believe the problem is at useEffect but I cannot figure out how to fix this.
I am looking forward to receiving your inspirations. Thanks and please stay safe.
---> Parent.js
import React, { useState, useEffect } from "react";
import Info from "./Info";
import Child from "./Child";
let Category = ["All", "First-Tier", "Second-Tier"];
const Parent = () => {
const [categoryChosen, setCategoryChosen] = useState("All");
let PartsShown = [...Info];
useEffect(() => {
PartsShown = [
...PartsShown.filter((e) => e.category[1] === categoryChosen),
];
}, [categoryChosen, PartsShown]);
return (
<div>
<div>
{Category.map((element) => (
<button
style={{ margin: 10 }}
key={element}
onClick={() => setCategoryChosen(element)}
>
{element}
</button>
))}
</div>
<div>{categoryChosen}</div>
<div>
{PartsShown.map((e) => (
<Child
key={e.name}
name={e.name}
category={e.category[1]}
/>
))}
</div>
</div>
);
};
export default Parent;
---> Child.js
import React from "react";
const Child = ({ name, category }) => (
<div style={{ margin: 10 }}>
<h1>{name}</h1>
<p>{category}</p>
<hr />
</div>
);
export default Child;
--> Info.js
const Info = [
{
name: "A",
description: "Description of A ",
category: ["All", "First-Tier"],
},
{
name: "B",
description: "Description of B",
category: ["All", "Second-Tier"],
}
];
export default Info;
import React, { useState } from "react";
import Info from "./Info";
import Child from "./Child";
const Category = ["All", "First-Tier", "Second-Tier"];
const Parent = () => {
const [partsShown, setPartsShownAndCategory] = useState({
partsArray: [...Info],
category: "All"
});
const changeCategory = category => {
const PartsShown = Info.filter(
element =>
element.category[1] === category || element.category[0] === category
);
setPartsShownAndCategory({
...partsShown,
category: category,
partsArray: PartsShown
});
};
console.log(partsShown);
return (
<div>
<div>
{Category.map(element => (
<button
style={{ margin: 10 }}
key={element}
onClick={() => changeCategory(element)}
>
{element}
</button>
))}
</div>
<div>{partsShown.category}</div>
<div>
{partsShown.partsArray.map(e => (
<Child key={e.name} name={e.name} category={partsShown.category} />
))}
</div>
</div>
);
};
export default Parent;