class App extends Component {
constructor() {
super()
this.state = {
firstName: ""
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(event) {
this.setState({
firstName: event.target.value
})
}
render() {
return (
<form>
<input type="text" placeholder="First Name" onChange={this.handleChange} />
<h1>{this.state.firstName}</h1>
</form>
);
}
}
export default App;
Hello all, I am currently studying React and seem to be having a hard time grasping all of it. The code that I have here works in that it will show in browser what the user is typing in the input box. What I cannot seem to figure out or get to work, is mapping what is typed in the input to stay on the screen. I.e. when I hit enter, it refreshes and the name goes away. I am trying to now create an unordered list to keep each name displayed on the screen. Any help or links would be greatly appreciated. Thank you
Just add new function (this describe what should be after submit this form) in this case You use:
event.preventDefault() -
The Event interface's preventDefault() method tells the user agent
that if the event does not get explicitly handled, its default action
should not be taken as it normally would be
onSubmit(event){
event.preventDefault()
}
and on form:
<form onSubmit={this.onSubmit}>
To create unordered list use something like this (credit for Robin Wieruch):
import React from 'react';
const initialList = [
'Learn React',
'Learn Firebase',
'Learn GraphQL',
];
const ListWithAddItem = () => {
const [value, setValue] = React.useState('');
const [list, setList] = React.useState(initialList);
const handleChange = event => {
setValue(event.target.value);
};
const handleSubmit = event => {
if (value) {
setList(list.concat(value));
}
setValue('');
event.preventDefault();
};
return (
<div>
<ul>
{list.map(item => (
<li key={item}>{item}</li>
))}
</ul>
<form onSubmit={handleSubmit}>
<input type="text" value={value} onChange={handleChange} />
<button type="submit">Add Item</button>
</form>
</div>
);
};
export default ListWithAddItem;
Related
I have a form component in my react app that can be used to create or update recipes depending on the route. The create functionality works fine, but the update is giving me trouble.
I pass fetched data into the component using props here is what the JSON looks like:
{
method: "Cook the guanciale in a large skillet over medium heat until deeply golden
(adjust the heat as necessary to render the fat [...]
name: "Pasta Alla Gricia"
}
I am trying to get the name to prefill into the form's name <input> and the method to prefill into the form's method <textarea>. I tried doing this with useEffect(), with:
useEffect(() => {
setName(props.data.name)
setMethodStepsList(props.data.method)
})
while it prefilled the name input it then locked the value to that. The method did not prefill the textarea at all.
I am pretty stumped with this one, and would be grateful for any assistance.
export default function Recipe(props) {
const [name, setName] = useState('')
const [methodStepsList, setMethodStepsList] = useState('')
const [methodStepObject, setMethodStepObject] = useState([])
const [ingredientList, setIngredientList] = useState([])
const [ingredientObject, setIngredientObject] = useState({
ingredient_name: '',
quantity: '',
measure: '',
})
const formLabel = props.data ? 'Update Recipe' : 'New Recipe'
useEffect(() => {
setName(props.data.name)
setMethodStepsList(props.data.method)
})
//new recipe logic
[...]
return (
<div>
<div className="recipe-form-container">
<form className="recipe-form">
<div className="page-header">
<h1>{formLabel}</h1>
</div>
{/* recipe name logic */}
<div className="recipe-title recipe-element">
<label>Recipe Name</label>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
></input>
</div>
//recipe method logic
<div className="recipe-blurb recipe-element">
<label>Recipe Method</label>
<span className="method-span">
<textarea
rows="5"
name="step_instructions"
type="text"
placeholder="Method will be split up based on new lines"
onChange={(e) => handleMethodChange(e)}
></textarea>
<button
onClick={(e) => {
console.log(methodStepObject)
setMethodStepsList(methodStepObject)
e.preventDefault()
}}
>
Add Method
</button>
[...]
}
Please remove useEffect statements and try like this
const [name, setName] = useState(props.data.name)
const [methodStepsList, setMethodStepsList] = useState(props.data.method)
You should be careful while initializing state with props in React.
See React component initialize state from props
class Recipe extends React.Component {
constructor(props) {
super(props)
this.state = {name:'', method:'', ingredients: []};
}
static getDerivedStateFromProps(props,state) {
return {name: props.data.name, method: props.data.method};
}
render() {
return <div>...</div>
}
}
I am trying to implement eye/eyeslash in on my Register form in React.
This is a function that's is responsible for changing visibility type and eye icon changing.
import React, { useState } from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
export const usePasswordToggle = () => {
const [visible, setVisibility] = useState();
const Icon = <FontAwesomeIcon icon={visible ? "eye-slash" : "eye"} />;
const InputType = visible ? "text" : "password";
return [InputType, Icon];
};
I am trying to implement it in component responsible for registering.
import React, { Component, createRef } from "react";
import { usePasswordToggle } from "./usePasswordToggle";
class Register1 extends React.Component {
EmailR = createRef();
UsernameR = createRef();
PasswordR = createRef();
PasswordConfirmR = createRef();
constructor(props) {
super();
this.state = {
message: "",
password: "",
confirmPassword: "",
};
}
handleSubmit = (event) => {
// alert(this.PasswordR.current.value);
// alert(this.PasswordConfirmR.current.value);
if (this.PasswordR.current.value !== this.PasswordConfirmR.current.value) {
alert("The passwords doesn't match");
return false; // The form won't submit
} else {
alert("The passwords do match");
return true; // The form will submit
}
};
onCreateAccount = () => {
let loginInfo = {
Username: this.UsernameR.current.value,
Email: this.EmailR.current.value,
Password: this.PasswordR.current.value,
};
fetch("http://localhost:5000/api/authenticate/register", {
method: "POST",
headers: { "Content-type": "application/json" },
body: JSON.stringify(loginInfo),
})
.then((r) => r.json())
.then((res) => {
if (res) {
this.setState({
message:
"New Account is Created Successfully. Check your email to verify Account.",
});
}
});
};
render() {
return (
<div>
<h2 className="FormDescription">
{" "}
Please enter Account details for registration
</h2>
<div className="Form">
<p>
<label>
Email: <input type="text" ref={this.EmailR} />
</label>
</p>
<p>
<label>
Username: <input type="text" ref={this.UsernameR} />
</label>
</p>
<div>
<label>
Password:{" "}
<input type={usePasswordToggle.InputType} ref={this.PasswordR} />
</label>
<span className="password-toogle-icon">
{usePasswordToggle.Icon}
</span>
</div>
<p>
<label>
ReenterPassword:{" "}
<input type="password" ref={this.PasswordConfirmR} />{" "}
</label>
</p>
<button onClick={this.handleSubmit}> Create </button>
<p>{this.state.message}</p>
</div>
</div>
);
}
}
export default Register1;
My password is always visible, and eye icon is even not visible on the form (it should be inside my input field, but it is not).
Focus on this code snippet:
<div>
<label>
Password: <input type={usePasswordToggle.InputType} ref={this.PasswordR} />
</label>
<span className="password-toogle-icon">{usePasswordToggle.Icon}</span>
</div>
Any suggestion what is the problem?
Change this
const [visible, setVisibility] = useState();
to this
const [visible, setVisible] = useState(true);
as the official documentation here
First, add a default value to your useState, either true or false depending on which icon you want to render first.
Then, you should add a onClick method to your icon which will toggle the visibility state. You're setting the icon based on visible value, but you never toggle the value.
onClick={() => setVisibility(!visible)}
UPDATE
You also need to execute your Hook inside your main component (because yes, you wrote what React call a Hook), like so :
const [inputType, icon] = usePasswordToggle();
But doing so, you'll get an error from React that say you cannot use a Hook within a class component due to how they work.
Basically you need to change your Register1 component to be a functional component, and not a class anymore. Look here for a quick overview on how to : https://reactjs.org/docs/components-and-props.html
Below is my code for a personal project where i can keep track of my monthly subscriptions, if i have to add a subscription i just have a add an object to an existing array. however for testing purposes when i tried to console.log(value.startDate) in handleSubmit it gives me undefined and causes further problems. How would i fix it?
import React from 'react';
import PropTypes from 'prop-types';
const List = () => {
const [ mylist, setList ] = React.useState([]);
const [ value, setValue ] = React.useState({ subscription: '', startDate: '', paymentTime: 0 });
const handleSubmit = (e) => {
console.log(value.startDate);
setList(mylist.push(value));
e.preventDefault();
};
const handleOnChange = (event) => {
setValue({ [event.target.name]: event.target.value });
};
return (
<div>
<div className="for_list">
<ul className="list">{mylist.map((obj) => <li key={obj.subscription}>{obj.subscription}</li>)}</ul>
</div>
<div className="for_form">
<form>
<input type="text" name="subscription" onChange={handleOnChange} value={value.subscription} />
<input type="text" name="startDate" onChange={handleOnChange} value={value.startDate} />
<input type="number" name="paymentTime" onChange={handleOnChange} value={value.paymentTime} />
</form>
</div>
<button onClick={handleSubmit}>Add Item</button>
</div>
);
};
// it just removes the error above.
List.propTypes = {
list: PropTypes.node
};
export default List;
You are replacing your state every time. This might be because of the miss in understanding the difference between setState in traditional class based React components and useState.
You need to append the value to the existing data. Something similar would work
const handleOnChange = (event) => {
setValue({ ...value, [event.target.name]: event.target.value });
};
The setState in class based components always accepts partial state and merges with the existing one. While useState setter function replaces the value you provide in the respective state.
On handleChange function you need to pass the old value of value
const handleOnChange = (event) => {
setValue({ ...value , [event.target.name]: event.target.value });
};
I want to grab the value of input inside the array when the button is clicked. How do i pass the input value to the function of button.
Any help would be appreciated. Thanks
import React, { useState, useEffect } from 'react'
export default function Todo(props) {
const [todo,settodo] = useState([]);
function getdata(){
//fetch data
settodo(data);
}
function SaveInput(id){
}
useEffect(() => {
getdata();
},[]);
return (
<React.Fragment>
<div>
{todo.map(function(item, key){
return <div>
<div>{item.name}</div>
<div>
<input type="text" name="inputval" onChange={() => handleChange(e)}>
<button onClick={()=> SaveInput(item.id)}></button>
</div>
</div>
})}
</div>
</React.Fragment>
)
}
You need to send item.id to your handleChange function,
<input type="text" name="inputval" onChange={(e) => handleChange(e,item.id)} />
You handleChange function should,
const handleChange = (e,id) => {
let val = e.target.value;
setInputVal(prevState =>({
...prevState,
[id]:val
}))
}
You must define a state to store input values,
const [inputVal,setInputVal] = useState({});
On the click of button you can access input state,
function SaveInput(id){
console.log(inputVal[id]);
}
Demo
You can save the inputs in a separate useState when the input is being changed, which can be later retrieved easily during the button click event.
Code below is an example and is not tested, but should give you some idea how to proceed.
import React, { useState, useEffect } from 'react'
export default function Todo(props) {
const [todo,settodo] = useState([]);
const [inputVal, setInputVal] = useState({});
function getdata(){
//fetch data
settodo(data);
}
function SaveInput(id){
let inputVal = inputVal[id];
// do other stuff.
}
useEffect(() => {
getdata();
},[]);
return (
<React.Fragment>
<div>
{todo.map(function(item, key){
return <div>
<div>{item.name}</div>
<div>
<input type="text" name="inputval" onChange={(e) => setInputVal({...inputVal, [item.id]: e.target.value })}>
<button onClick={()=> SaveInput(item.id)}></button>
</div>
</div>
})}
</div>
</React.Fragment>
)
}
One common pattern is to use the handleChange(event) function on input to set a state with the current value.
const [input,setInupt] = useState("");
function handleChange(event) {
setInput(event.target.value)
}
and when the button is clicked, you can use the value of the input state to pass on
<button onClick={()=> console.log(input))}>
First of all, If you are having an onChange method then you must have a value for that input as well or else it will display a warning for "uncontrolled input" and that input box is of no use to you unless you provide a value to it.
Secondly, you should use a state for the values of those input boxes and then you can access the values of input in the save button click function. Here is the example of how you can do it.
import React from 'react'
export default class Todo extends React.Component {
constructor(props) {
super(props);
this.state = {
inputIDs: {}
}
}
SaveInput = id => {
console.log("input value:", this.state[id]);
};
handleChange = (e, id) => {
this.setState({[id]: e.target.value});
};
render() {
const {inputIDs} = this.state;
const todo = [
{id: 1, val: "abc", name: "lorem"},
{id: 2, val: "xyz", name: "Ipsum"}
];
let todos = todo.map((item, key) => {
return <div key={key}>
<div>{item.name}</div>
<div>
<input type="text" value={this.state[item.id]} onChange={(e) => this.handleChange(e, item.id)}/>
<button onClick={() => this.SaveInput(item.id)}>Click Me!</button>
</div>
</div>
});
return (
<React.Fragment>
{todos}
</React.Fragment>
)
}
}
I have a page where a user can search a database for a given condition, then the data is returned with another button that the user can use to add information back to the database. However whenever I click on the second button, the page reloads. I can't get so much as a console.log to go in. I'm new to react and could use any help at all.
import React , { Component } from 'react';
import { database } from '../firebase';
const byPropKey = (propertyName, value) => () => ({
[propertyName]: value,
});
class Search extends Component{
constructor(props) {
super(props);
this.state={
users: null,
searchCondition: "",
friend: ""
}
// this.setState = this.setState.bind(this);
}
onSubmit = (event) => {
let {
searchCondition,
friend
} = this.state;
database.searchConditions(searchCondition).then(snapshot =>
this.setState(() => ({ users: snapshot.val() }))
);
event.preventDefault();
}
messageSubmit = (event) => {
console.log("Click")
}
render(){
let {
users,
searchCondition,
friend
} = this.state;
return(
<div>
<h1>Search for conditions</h1>
<form onSubmit={this.onSubmit}>
<div className="search">
<input
value={searchCondition}
onChange={event => this.setState(byPropKey('searchCondition', event.target.value))}
type="text"
placeholder="Condition to Search For"
/>
<button className="friendButton"
onClick="x"
type="submit">
Search
</button>
</div>
</form>
{!!users && <UserList users={users} />}
</div>
)
}
}
let UserList = ({ users, message }) =>
<div>
<h2>List of Usernames and Conditions of your Search</h2>
{Object.keys(users).map(key =>
<div key={key}>{users[key].username} : {users[key].condition}
<form>
<div className="search">
<input
value={message}
onChange={console.log("test")}
type="text"
placeholder="Message for this User"
/>
<button className="messageButton"
onClick={console.log(message)}
type="submit">
Message
</button>
</div>
</form>
</div>
)}
</div>
export default Search;
Have you tried to place the event.preventDefault() at the beginning of the event handler?
It should prevent the default behaviour imediately as the event gets fired.
Hope it works!
a couple things i can see, youre even.preventDefault() should be at the top of the page, you said it was reloading so thats unwanted behavior. second you should set state within the then, generally speaking in my experience that doesnt work- i believe due to setState being asynchronous or something of that nature.
i would rewrite your submit like this
onSubmit = (event) => {
event.preventDefault();
let {
searchCondition,
friend
} = this.state;
let value;
database.searchConditions(searchCondition).then(snapshot =>
value = snapshot.val
);
this.setState(() => ({ users: value) }))
}
also likely the reason your "messageSubmit()" was not console logging is because youre using a submit handler not a click handler so everytime your clicked you were reloading the page.
cheers