I've been trying to debug this for hours and looked at every single other Stack Overflow question that has the same style of issue, but they all just say to use keys and that's still not working for me. I've made a simpler example of my code that replicates the error.
import React from 'react'
import { useState } from 'react'
const FormTest = () => {
const [name, setName] = useState('')
const TestExperience = (props) => {
return (<div>
<h1>Test Experience {props.num}</h1>
<input
type="text"
name="name"
placeholder="Name"
/>
</div>)
}
const processFormData = (event) => {
event.preventDefault();
console.log(event.target);
}
const [nums, setNums] = useState([1, 2, 3]);
const arr = nums.map((num, index) => <TestExperience num={num} key={index}/>);
return (
<div>
<form onSubmit={(event) => {
processFormData(event);
}}>
{arr}
<button onClick={() => {
setNums([...nums, nums.length + 1]);
}}>Add one!</button>
</form>
</div>
)
}
export default FormTest
I've tried moving TestExperience to it's own, separate function. I'm trying to basically have inputs that one can create more of, and this issue of losing focus came from the fact that every time an input was added, all of the existing formData disappeared due to a re-render. The goal would be to just use the onSubmit function to parse the data, but since it disappears after adding the input I figured I needed to store it. I've been going down rabbit hole after rabbit hole trying to fix what seems like such a simple problem and just keep running into issues with every implementation I try.
The overall goal is that I have a submit button and an add input button, and I tried to ditch the whole value={stateVariable} and onChange={setStateVariable} thing and just make the input button a "submit" button so that I can run the processFormData and do different things based on which submit button it was, but I have no clue how to check which button the submit came from when there's two different buttons, so an answer to that could be super helpful as well because then I can avoid this whole state mess.
You need to move the TestExperience out of FormTest.
import React from "react";
import { useState } from "react";
const TestExperience = (props) => {
const [name, setName] = useState("");
return (
<div>
<h1>Test Experience {props.num}</h1>
<input
type="text"
name="name"
placeholder="Name"
onChange={(event) => {
event.preventDefault();
setName(event.target.value);
}}
value={name}
/>
</div>
);
};
const FormTest = () => {
const processFormData = (event) => {
event.preventDefault();
console.log(event.target);
};
const [nums, setNums] = useState([1, 2, 3]);
const arr = nums.map((num, index) => (
<TestExperience num={num} key={index} />
));
return (
<div>
<form
onSubmit={(event) => {
processFormData(event);
}}
>
{arr}
//another way I tried to do it below //
{nums.map((num, index) => (
<TestExperience num={num} key={index} />
))}
<button
onClick={() => {
setNums([...nums, nums.length + 1]);
}}
>
Add one!
</button>
</form>
</div>
);
};
export default FormTest;
Code sandbox => https://codesandbox.io/s/trusting-elbakyan-mxrez?file=/src/App.js
Related
This question already has an answer here:
ReactJS, event.currentTarget doesn't have the same behavior as Vanilla Javascript
(1 answer)
Closed last month.
I want to capture the name attribute of a button on click in React.
I tried the following code block:
export function TestButton(props){
function logName() {
console.log(this.name)
}
return(
<button name={props.name} onClick={event => logName(event.currentTarget.getAttribute("name"))} type='button'>{props.text}</button>
)
}
My expectation was that this code would allow me to create a button that displays the name in the console log:
<TestButton name='helloWorld' text='Click Me'/>
Instead I get an alert that this is undefined. This is in spite of my ability to see the name when I inspect the element.
I have also tried target instead of currentTarget with no luck. I also tried event.currentTarget.name without the results I desire.
What did i miss?
In react, I believe this is reserved for classes, whereas you are defining a functional component. In a functional component, the comparable state value would be stored with useState(). That being said, I'm not sure I see the need for that here, since this button is getting its props from somewhere and the value of name and text are not changing in this component. I would code it this way:
export const TestButton = ({props}) => {
return(
<button name={props.name} onClick={() => console.log(props.name)}>
{props.text}
</button>
)
}
Now to go a bit further, maybe you want to use state wherever this button is being rendered. That could look like this:
import {TestButton} from "./someFile";
const [name, setName] = useState("some-button");
const [text, setText] = useState("click me!");
// now there could be some code here that decides what the name or text would be
// and updates the values of each with setName("name") and setText("text")
const Page = () => (
<>
<TestButton props={{name: name, text: text}} />
</>
)
This is all building off your current code, but now I will combine everything in a way that makes sense to me:
import {useState} from "react";
const [name, setName] = useState("some-button");
const [text, setText] = useState("click me!");
// some code to determine/change the value of the state vars if necessary
const TestButton = ({name, text}) => {
return(
<button name={name} onClick={() => console.log(name)}>
{text}
</button>
)
}
export const Page = () => (
<>
<TestButton name={name} text={text} />
</>
)
Pleas try as follows:
export function TestButton(props){
function logName() {
console.log(props.name)
}
return(
<button name={props.name} onClick={() => logName()} type='button'>{props.text}</button>
)
}
Try this
export function TestButton(props){
const logName = (e, name) => {
console.log("name attribute ->", name)
}
return(
<button name={props.name} onClick={ (e) => logName(e, props.name)} type='button'>{props.text}</button>
)
}
I'm following this tutorial on YouTube https://youtu.be/b9eMGE7QtTk
The full code can be found here: https://gist.github.com/adrianhajdin/997a8cdf94234e889fa47be89a4759f1
The tutorial was great, but it didn't split all the functionalities into components which is React used for (or I'm so lead to believe).
So we have the App.js
import React, { useState, useEffect } from "react";
import MovieCard from "./MovieCard";
import SearchIcon from "./search.svg";
import "./App.css";
const API_URL = "http://www.omdbapi.com?apikey=b6003d8a";
const App = () => {
const [searchTerm, setSearchTerm] = useState("");
const [movies, setMovies] = useState([]);
useEffect(() => {
searchMovies("Batman");
}, []);
const searchMovies = async (title) => {
const response = await fetch(`${API_URL}&s=${title}`);
const data = await response.json();
setMovies(data.Search);
};
return (
<div className="app">
<h1>MovieLand</h1>
<div className="search">
<input
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search for movies"
/>
<img
src={SearchIcon}
alt="search"
onClick={() => searchMovies(searchTerm)}
/>
</div>
{movies?.length > 0 ? (
<div className="container">
{movies.map((movie) => (
<MovieCard movie={movie} />
))}
</div>
) : (
<div className="empty">
<h2>No movies found</h2>
</div>
)}
</div>
);
};
export default App;
MovieCards.jsx is as follows:
import React from 'react';
const MovieCard = ({ movie: { imdbID, Year, Poster, Title, Type } }) => {
return (
<div className="movie" key={imdbID}>
<div>
<p>{Year}</p>
</div>
<div>
<img src={Poster !== "N/A" ? Poster : "https://via.placeholder.com/400"} alt={Title} />
</div>
<div>
<span>{Type}</span>
<h3>{Title}</h3>
</div>
</div>
);
}
export default MovieCard;
The app works, but I want to move className="search" to be its own component like Search /.
The code I end up having in App.js is
//at the top of App.jx
import Search from "./Search"
// in const App
<Search prop={searchMovies}/>
And in the new Seach / component
import { useState } from "react";
import SearchIcon from './search.svg';
const Search = ( prop ) => {
const [searchTerm, setSearchTerm] = useState("");
return (
<div className="search">
<input
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search"
/>
<img
src={SearchIcon}
alt="search"
onClick={() => prop(searchTerm)}
//props used to be searchMovies
/>
</div>
)
}
export default Search;
When typing something in the search field on the app and clicking on the search icon I get the following error:
prop is not a function
If my research has been correct, I need to use a constructor and super()
But it seems like the constructor needs to be called in a class Search instead of const Search as it breaks the code. Is that the case or is there a way to use the constructor in a function component, or is there something else completely that I should do?
Also, if there is a great tutorial you could recommend for super() I'd be really grateful.
Other thing that I want to do is to make a Results component or call it whatever that would have the {movies?.length > 0 ? ( part of the code, but I feel like that will be a different headache.
Basically what I want is to have:
const App = () => {
return (
<div className="app">
<h1>Movie Site</h1>
<Search />
<Results />
</div>
);
};
Or as shown in the picture
Hope all this makes sense. Also, I want to preface that I do not expect anyone to write the code for me, but if it helps me understand this it's appreciated. YT tutorials are appreciated as well.
Okay, after a push in the right direction from jonrsharpe and renaming the props into random things I figured it out.
As jonrsharpe said, my function is prop.prop, so if I wanted to call searchTerm in
onClick={() => prop(searchTerm)}
it should be
onClick={() => prop.prop(searchTerm)}
Now, that works, but looks silly. So renaming the first "prop" in prop.prop and the prop in const Search to searchOnClick leaves searchOnClick.prop(searchTerm) which still works. Great.
Then in App.js renaming prop in Search prop={searchMovies} to searchOnClick={searchMovies} needs to be followed by renaming searchOnClick.prop in Search.jsx to searchOnClick.searchOnClick.
Lastly, we want to destructure the props as jonrsharpe said.
const Search = ( searchOnClick ) => {
would become
const Search = ( {searchOnClick} ) => {
That allows us to remake searchOnClick.searchOnClick(searchTerm) to searchOnClick(searchTerm) only.
The whole point is that the prop calls the whole componentName variable=value but it doesn't take the value of the variable automatically so it needs to be called like prop.variable until destructured where it can be called as variable only.
Now that I figured this out it feels silly spending two days on this. Thanks to jonrsharpe again, and hope this helps to someone else in the future.
react newbie here once again.
I'm trying to update the page with a note when pressing 'send' button.
Honestly I tried for so long that I think my code is pretty much a mess rn.
Main func:
function MainLogged() {
const [sendingScreen, setSendingScreen] = useState(true);
let notes = [];
const [note, setNote] = useState('')
function onNoteChange(event){
const {value} = event.target;
setNote(value);
}
function afterButtonClick(event) {
notes.push(note);
setSendingScreen((prev) => !prev)
}
return (
<div id='main'>
{sendingScreen ? <MainBody noteCallback={onNoteChange} callback={afterButtonClick}/> : <ResetScreen callback={afterButtonClick}/> }
{console.log(notes)}
{
notes.map((singleNote) => {
return (<p id='single-note' className='note'>{singleNote}</p>)
})
}
</div>
)
}
then goes to component:
function MainBody(props) {
const {noteCallback, callback} = props;
return (
<div>
<p id='main-body'>{lorem}</p>
<div className='card text-center' style={{width: '18rem', alignSelf: 'center'}}>
<Input onChange={noteCallback} type='text' placeholder='My note is...' className='card-body information' />
</div>
<SendCardButton callback = {callback}/>
</div>
)
}
and finally (if necessary):
function SendCardButton(props){
const {callback} = props;
return (
<button type="button" className="btn btn-success toBeCentered" onClick={callback}>Send</button>
)
}
Using this code leaves notes array empty always.
Please if you can explain so I can learn and improve, I REALLY need it right now.
Thanks a lot!
React does not rerender your component after you click the button because notes are not stored in state. You need declare notes this way:
const [notes, setNotes] = useState([]);
And then set them on button click, not just push the new one:
setNotes((prevNotes) => ([...prevNotes, note]));
That should be enough:) Feel free to comment if you have further questions
I'm trying to create a function that renders an array of links and i want to create a text input and a button that adds value from input in the array. I got the links saved in the state in the object that looks like this:
sourceLinks: {
0: "https://www.w3schools.com/html/"
1: "https://www.apachefriends.org/docs/"
2: "https://docs.moodle.org/38/en/Windows_installation_using_XAMPP"
}
I've managed to render the links like this:
renderLinks() {
let sessionLinks = this.state.sessionLinks;
let links = [];
Object.values(sessionLinks).map((link) => {
links.push(<div className="column">
<span>
<InputPreview inputValue={link} classes="w-300" />
</span>
</div>)
})
return links;
}
InputPreview is the component i use for displaying links. I'm tryin to add a text input and a button bellow the rendered links that adds the value to the array, and an icon next to every link that removes it from an array. I'm trying to do it all in one function renderLinks() and then call it in render. I know i have to push and slice items from an array and update the state but i'm strugling cause i just started learning react. Please help :)
You can add and render links with below code.
import React from "react";
class ItemList extends React.Component {
state = {
links: ["item1"],
newItem: ""
};
submit(e, newLink) {
e.preventDefault();
let updatedLinks = this.state.links;
updatedLinks.push(newLink);
this.setState({ links: updatedLinks });
}
render() {
return (
<React.Fragment>
<ul>
{this.state.links?.map((link, i) => (
<li key={i}>
<p>{link}</p>
</li>
))}
</ul>
<form onSubmit={(e) => this.submit(e, this.state.newItem)}>
<input
type="text"
value={this.state.newItem}
onChange={(e) => this.setState({ newItem: e.target.value })}
/>
<button type="submit">ADD</button>
</form>
</React.Fragment>
);
}
}
export default ItemList;
Let me know for further clarificaton.
This is a example with functional components and hooks
import React, { useState } from 'react';
const sourceLinks = [
'https://www.w3schools.com/html/',
'https://www.apachefriends.org/docs/',
'https://docs.moodle.org/38/en/Windows_installation_using_XAMPP',
];
export const ListLinks = () => {
const [links, setLinks] = useState(sourceLinks);
const [newLink, setNewLink] = useState('');
const handleAdd = () => {
setLinks(links => [...links, newLink]);
};
const handleChangeNewLink = e => {
const { value } = e.target;
setNewLink(value);
};
return (
<div>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<input type='text' value={newLink} onChange={handleChangeNewLink} />
<button onClick={handleAdd}>Add</button>
</div>
<br />
{links.map((link, index) => (
<p key={index}>{link}</p>
))}
</div>
);
};
This is the result:
Lastly, read the documentation, managing the state is essential.
Good afternoon everyone.
I have a dropdown with two input fields inside. Name and Price.
I would like to display the name and price after I click Set button that it appears in the same dropdown but on top of input fields.
Here is how it looks in my app currently, I enter name and price by myself.
As you can see in a first field there is a name and in the second there is a number and I wan't to store it under Price Alert History after clicking Set button.
Here is how I wish it will look. It's just an example which was made in photoshop. The main thing that I want to see name and price on top of input field.
CODE HERE
import React from "react";
import { Button} from "react-bootstrap";
const symbols = [
"ADABTC",
"AIONBTC",
"ALGOBTC",
"ARDRBTC",
"KAVABTC",
"ETHBTC",
"ETCBTC"
];
function PriceTriggerField() {
const [searchTerm, setSearchTerm] = React.useState("");
const [searchSymbol, setSearchSymbol] = React.useState([]);
const handleChangeTerm = event => {
setSearchTerm(event.target.value);
};
const handleChangeSymbol = event => {
setSearchSymbol(event.target.value);
};
React.useEffect(() => {
const results = symbols.filter(symbols =>
symbols.toUpperCase().includes(searchTerm)
);
setSearchSymbol(results);
}, [searchTerm]);
return (
<div className="App">
<h6>Price Alert History</h6>
<input
type="text"
placeholder="Symbol"
value={searchTerm}
onChange={handleChangeTerm}
/>
<input
type="number"
placeholder="Price"
/>
{
searchTerm.length > 0 && searchSymbol.map(item => <li onClick={(() => setSearchTerm(item) )}>{item}</li>)
}
<Button variant="secondary">Set</Button>
</div>
);
}
export default PriceTriggerField;
this is just a simple example with only one variable, but of course, you can do that for as many variables as you wish.
import React, { useState } from "react";
export default function App() {
const [name, setName] = useState(null);
let tmpName;
const onChange = e => {
tmpName = e.target.value;
}
return (
<div className="App">
<input onChange={onChange} />
<button onClick={() => setName(tmpName)}>set</button>
name: {name}
</div>
);
}