I have working code that helps me create a to-do list. Everything works almost well. When I click on the "Enter" key, my page reloads. Similarly, after reloading the page, all created elements disappear. I have 2 questions: can you show how to save all created elements after reloading and how to avoid reloading by pressing on "Enter"? Thank you very much
import React, {useState} from "react";
export function Creating_List () {
let [allTasks, setAllTasks] = useState([]);
let [input, setInput] = useState('');
let addTask = (myInput) => {
if (myInput){
let newTask = {
id: Math.random().toString(36).substr(2,9),
task: myInput,
complete: false
}
setAllTasks([...allTasks, newTask])
}
}
let taskDone = (id) => {
setAllTasks([allTasks.filter((todo => todo.id !== id))])
}
let handleInput = (e) => {
setInput(e.currentTarget.value)
}
let submitTask = (e) => {
e.preventDefault();
addTask(input);
setInput('');
}
return (<div className='tasks'>
<h1>Список задач {allTasks.length}</h1>
<form>
<input
type="text"
value={input}
onChange={handleInput}
placeholder="Нове завдання"
/>
</form>
<button onClick={submitTask} type="submit">Створити</button>
<div>
{allTasks.map(el => <div key={el.id}>{el.task} <button onClick={taskDone}>Виконано</button> </div>)}
</div>
</div>)
}
Change your <form> to <form onSubmit = {submitTask}>, this will prevent the page from reloading on enter since you have e.preventDefault(). If you want data to persist after reloading, you can use localStorage or a database like Firebase or MongoDB, or you can create your own backend using Node.js.
Related
How do you save a text that user typed in reactjs?
import React, {useState} from "react";
import "./ToDo.css";
const ToDo = () => {
const [input, setInput] = useState('');
const HandleInputChange = (event) => {
setInput(event.target.value);
}
const SaveInput = () => {
setInput(document.innerText = input)
}
const DeleteInput = () => {
setInput('');
}
return(
<div>
<input type={'text'} value={input} onChange={HandleInputChange}/>
<button onClick={SaveInput}>✓</button>
<button onClick={DeleteInput}>X</button>
<br/>
<br/>
<ul>
<li>{input}</li>
</ul>
</div>
);
}
export default ToDo;
I want to know how to save a user typed in a input and save it and display it to the user
I got your question, you wanna save input into List Field. You first need to save input into Localstorage of browser and then mapping all input in the list...
Changes in code like-
const SaveInput = () => {
setInput(document.innerText = input)
}
in the above arrow function try to set localstorage with user input and also relaod the page onClick..
After saving input in localstorage now next step is to get value from then mapping it...
Hope above solution helping you in getting your result. If you still facing any issue just lemme know..
Thanks
I am trying to disable a button after it is clicked, but it is not holding its disabled tag. Another weird thing is that if I click the button twice it will disable. Code below
const [loading, setLoading] = useState('Submit');
...
<form onSubmit={(event) => {
event.preventDefault();
submitBet(units, line, team, gameID);
}}>
...
<button type='submit' className='submit-betslip' id='submit-button-id'>{loading}.</button>
</form>
The loading variable is a useState. The onSubmit function:
const submitBet = async (units, line, team, id) => {
if (Number(units) === 0 && Number(line) === 0) {
console.log('Empty input');
return
}
try {
document.getElementById('submit-button-id').disabled = true;
}
...
}
Not sure if it matters but the form is within a React function. Any thoughts?
Here is a simple example to what you want to do and it's working perfectly.
import React from "react";
import "./styles.css";
export default function App() {
const [loading, setLoading] = React.useState("Submit");
const handleSubmit = (e) => {
e.preventDefault();
setLoading("Submitting..");
setTimeout(() => {
alert("submitted Successfully");
setLoading("Submit");
}, 3000);
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<form onSubmit={handleSubmit}>
<input id="f1" name="f1" />
<button type="submit" disabled={loading !== "Submit"}>
{loading}.
</button>
</form>
</div>
);
}
Simply link the disable prop to the loading state.
If that is not what you want to do, just share the component with us, and I would love to help.
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
I'm using a shorten URL API when the user passes a valid link, i fetch API and render the shortened URL with "map medthod" to make them into a list. It has a btn next to each mapped "shortened URL" where onClick i try to copyToClipboard and change state of btn from Copy to Copied. The problem is currently it only works fine if i have 1 item(on click btn works fine with copyToClipboard) but if i have 2 buttons and i click the very 1st btn to copyToClipboard it's focusing the last item in mapped list and copying the value of (last item) 2nd btn and also setting state for all btns to copied. I also don't understand why i can't see li tags with keys in console when i pass them the keys. can someone help me out. I just want to copyToClipboard that input value of the btn i have clicked. here's what it looks like - image of onCLick of 1st btn 2nd btn gets focus & image of no keys in console & apparently they aren't in a list?
Here is the code below
import { useForm } from "react-hook-form";
import axios from 'axios';
import Loading from '../../images/Ripple-1s-200px.svg';
const Shorten = () => {
// get built in props of react hook form i.e. register,handleSubmit & errors / watch is for devs
const { register, handleSubmit, formState: {errors} } = useForm();
//1. set user original values to pass as params to url
const [link, setLink] = useState('');
//2. set loader initial values to false
const [loading, setLoading] = useState(false);
//3. pass the fetched short link object into an array so we can map
const [displayLinks, setDisplayLinks] = useState([]);
//4. onSubmit form log data into link & showLoader for a breif moment
const onSubmit = (data, event) => {
event.preventDefault();
//puttin data in a variable to pass as url parameter if valid
setLink(data.userLink);
//add loading here after data is set to state
setLoading(!false);
}
//5. fetch the shortened url link using async method to show loading
useEffect(() => {
let unmounted = false;
async function makeGetRequest() {
try {
let res = await axios.get('https://api.shrtco.de/v2/shorten', { params: { url: link } });
//hid loader if u get response from api call
if (!unmounted && res.data.result.original_link) {
setLoading(false);
//add the data to displayLinks array to map
return setDisplayLinks(displayLinks => [...displayLinks, res.data.result]);
}
}
catch (error) {
console.log(error, "inital mount request with no data");
}
}
//invoke the makeGetRequest here
makeGetRequest();
return () => {
unmounted = true;
}
//passing dependency to re-render on change of state value
}, [link]);
//6. intial State of copied or not button
const [copySuccess, setCopySuccess] = useState('Copy');
const inputRef = useRef(null);
//7. onCick of button target it's short url right now it's selecting the last element
const copyToClipboard = (e) => {
e.preventDefault();
inputRef.current.select();
document.execCommand('copy');
// This is just personal preference.
setCopySuccess('Copied');
};
console.log(displayLinks);
return (
<div>
<form onSubmit={handleSubmit(onSubmit)}>
<label></label>
<input
{...register("userLink", {required: "Please add a link"})}
type="url"
id="userLink"
/>
{errors.userLink && <span>{errors.userLink.message}</span>}
<input type="submit" />
</form>
{
loading ?
<div className="loader" id="loader">
<img src={Loading} alt="Loading" />
</div>
: <ul>
{
displayLinks.map((el) => {
return (
<li key={el.code}>
<div>
<h5>{el.original_link}</h5>
</div>
{
/* Logical shortcut for only displaying the
button if the copy command exists */
document.queryCommandSupported('copy') &&
<form>
<input
ref={inputRef}
defaultValue={el.full_short_link}>
</input>
<button onClick={copyToClipboard}>{copySuccess}</button>
</form>
}
</li>
)
})
}
</ul>
}
</div>
)
}
export default Shorten;
Its because you are using a single ref for all the links
You are looping over all the links and giving their <input ref={inputRef} />.So the ref will always be attached to the last link input
Maybe don't use refs and use an alternative copyToClipboard function
like this one
const copyToClipboard = (url) => {
const textField = document.createElement('textarea')
textField.innerText = url
document.body.appendChild(textField)
if (window.navigator.platform === 'iPhone') {
textField.setSelectionRange(0, 99999)
} else {
textField.select()
}
document.execCommand('copy')
textField.remove()
setCopySuccess('Copied');
}
OR
Use a library like react-copy-to-clipboard
Also please go through this link
I hate to upload a code snippet with no sandbox, but this particular instance I use firebase so wasn't sure how to make one. Apologies for the verbose code. I'm a beginner React developer and I've been stuck on this state management issue for 2 weeks now, and I tried so many different methods but to no fruit. Please help.
My goal is to click AddLinkButton to make multiple input forms one by one, each input form would be different links, and by clicking Apply Button it would collect all the link values and store it to firebase's firestore. Once the storing is complete, it would display a preview by passing in multiple updated hook values to <UserPreview />.
If I run this particular code below, the key which is supposed to be the value of the link input forms, is null and does not update on onChange.
Please help... much appreciated. Thank you.
EDIT: changed variable name key to keyHook but to no success. Same issue
const AdminCustomizer = () => {
const [username, setUsername] = useState(null);
const [linkForm, setlinkForm] = useState([]);
const [spotlightLabel, setSpotlightLabel] = useState('');
const [spotlightLink, setSpotlightLink] = useState('');
const [refresh, setRefresh] = useState(false);
const [keyHook, setKeyHook] = useState(null);
const [startCollect, setStartCollect] = useState(false);
const linkRef = useRef();
const userInfo = {username, linkRef, spotlightLabel, spotlightLink, pfpURL, refresh};
// on initial load, load database to page
if (!username) {
firebase.getAuth().onAuthStateChanged(user => {
if (user) {
setUsername(user.displayName);
firebase.getUserInfo(user.displayName).then(result => {
setSpotlightLabel(result.spotlightLabel);
setSpotlightLink(result.spotlightLink);
linkRef.current = result.links;
if (result.links) {
Object.values(result.links).forEach(link => {
AddLinks(link);
});
}
})
}
});
}
//on refresh (when clicking apply changes button) reload page values with updated database
useEffect(() => {
if (refresh) {
firebase.getAuth().onAuthStateChanged(user => {
if (user) {
firebase.getUserInfo(user.displayName).then(result => {
linkRef.current = result.links;
Object.values(result.links).forEach(link => {
AddLinks(link);
});
})
setRefresh(false);
}
});
}
}, [refresh])
// adding AddLink button will add a new input form
// adding AddLink with firebase database value will add a new input form with values loaded
const AddLinks = url => {
const hooks = { refresh, startCollect, keyHook, setKeyHook };
if (url) setKeyHook(url);
setlinkForm([ ...linkForm, <AddLink key={keyHook} keyHook={keyHook} hooks={hooks} /> ]);
}
// add link input form
const AddLink = props => {
const handleChange = e => setKeyHook(e.target.value);
return (
<form noValidate autoComplete="off">
<br />
<Link label="Social" onChange={handleChange} value={props.keyHook} />
</form>
)
}
// when apply changes is clicked, collect input values from all link input forms
if (startCollect) {
linkForm.forEach(form => {
linkRef.current = {
...linkRef.current,
link: form.keyHook,
}
});
firebase.addLinksToUser({ spotlightLabel, spotlightLink, linkRef }).then(() => {
//force refresh to update userInfo for UserPreview
setStartCollect(false);
setRefresh(true);
});
}
return (
<>
<LinkBox>
<ApplyButton onClick={() => setStartCollect(true)}>Apply Changes</ApplyButton>
<Link label="Website Title" onChange={e => setSpotlightLabel(e.target.value)} value={spotlightLabel} />
<Link label="Website URL" onChange={e => setSpotlightLink(e.target.value)} value={spotlightLink}/>
<AddLinkButton onClick={() => AddLinks(null)} />
<div>{linkForm ? linkForm.map(child => child) : null}</div>
</LinkBox>
<div>
<PhoneOutline>
<UserPreview userInfo={userInfo}/>
</PhoneOutline>
</div>
</>
);
}
export default AdminCustomizer;
In AddLink, the key is a restricted keyword and doesn't get propagated as props. Try a different prop name instead of key.
See this link
Try:
<AddLink key={keyHook} keyHook={keyHook} hooks={hooks} />