I'm trying to make a note taking app in React.
I have managed to save state on local storage.
My goal is to display the local storage 'notes' in the textarea on render and refresh. So far on refresh the place holder is displayed on render.
I want to:
If no notes in local storage display place holder
If notes are present in local then display them in the text area.
Heres the code:
const [notes, setNotes] = useState("")
useEffect(() => {
const notes = localStorage.getItem("notes")
if (notes) {
setNotes(JSON.parse(notes))
}
})
const handleChange = e => {
setNotes(e.target.value)
localStorage.setItem("notes", JSON.stringify(e.target.value))
}
return (
<form>
<label for="pad">
<span>Add your notes</span>
<textarea
rows="10"
placeholder="Add notes here ๐"
name="pad"
onChange={handleChange}
></textarea>
</label>
</form>
)
Use textarea value with notes variable.
You don't need useEffect for this example.
Even if you want to use it please add your dependency array.
export default function App() {
const localNotes = localStorage.getItem("notes");
const [notes, setNotes] = useState(localNotes);
const handleChange = e => {
localStorage.setItem("notes", e.target.value);
setNotes(e.target.value);
};
return (
<form>
<label for="pad">
<span>Add your notes</span>
<textarea
rows="10"
placeholder="Add notes here ๐"
name="pad"
value={notes}
onChange={handleChange}
/>
</label>
</form>
);
}
Working example https://codesandbox.io/s/still-moon-bmuof
1) You should bind the notes state to TextArea's value props.
2) You need to add an empty array as the dependency array for the useEffect hook, such that the method within it will only run once when the component is rendered. This will ensure that the state is updated with the values from localStorage when the component is mounted.
const [notes, setNotes] = useState("")
useEffect(() => {
const notes = localStorage.getItem("notes")
if (notes) {
setNotes(notes)
}
}, [])
const handleChange = e => {
const { value } = target
setNotes(e.target.value)
localStorage.setItem("notes", value))
}
return (
<form>
<label for="pad">
<span>Add your notes</span>
<textarea
rows="10"
placeholder="Add notes here ๐"
name="pad"
onChange={handleChange}
value={notes}
></textarea>
</label>
</form>
)
Related
I am creating a react element which allows users to send emails to friends/family. The element defaults to 3 input fields however, the user can click a button to add additional input fields.
My idea to build this component is to have a button which increments a counter and then to use that counter value to run a loop and add items to an array
<button onClick={handleAddEmail}> Add email </button>
function handleAddEmail() {
setNumberOfEmails(numberOfEmails + 1);
}
Now for this to work I need an NumberOfEmails hook. This hook will be initialized with the first three input fields.
const [numberOfEmails, setNumberOfEmails] = useState(3);
Additionally there is a inviteEmails hook to handle the state of each input.
const [inviteEmails, setInviteEmails] = useState([])
All of these inputs should be rendered in a function:
function renderEmailFields() {
const emailContainer = [
<input
value={inviteEmails[0]}
onChange={(e) => handleEmailChange(0, e)}
placeholder="Enter email here..."
/>,
<input
value={inviteEmails[1]}
onChange={(e) => handleEmailChange(1, e)}
placeholder="Enter email here..."
/>,
<input
value={inviteEmails[2]}
onChange={(e) => handleEmailChange(2, e)}
placeholder="Enter email here..."
/>];
for (let i = 3; i < numberOfEmails; i++) {
emailContainer.push(
<input
value={inviteEmails[i]}
onChange={(e) => handleEmailChange(i, e)}
placeholder="New email..."
/>
);
}
return emailContainer;
The code I have above is close to working however adding a new input field causes the previous inputs to lock and become uneditable.
Any advice would be appreciated.
You don't really need a second state atom for the number of inputs if you ask me.
See live CodeSandbox here.
The useCallback and dataset magic make it so you don't need a separate onClick handler function for each input.
.filter(Boolean) removes all falsy values; empty strings are falsy.
For the heck of it, this also lets you remove items from the array.
function App() {
const [inviteEmails, setInviteEmails] = React.useState(["", "", ""]);
const handleEmailChange = React.useCallback((event) => {
const index = parseInt(event.target.dataset.index, 10);
setInviteEmails((inviteEmails) => {
const newInviteEmails = [...inviteEmails];
newInviteEmails[index] = event.target.value;
return newInviteEmails;
});
}, []);
const removeEmail = React.useCallback((event) => {
const index = parseInt(event.target.dataset.index, 10);
setInviteEmails((inviteEmails) => {
const newInviteEmails = [...inviteEmails];
newInviteEmails.splice(index, 1);
return newInviteEmails;
});
}, []);
const addEmail = React.useCallback(
() => setInviteEmails((inviteEmails) => [...inviteEmails, ""]),
[]
);
return (
<div>
{inviteEmails.map((email, index) => (
<div key={index}>
<input
value={email}
data-index={index}
onChange={handleEmailChange}
placeholder="Enter email here..."
/>
<button onClick={removeEmail} data-index={index}>
ร
</button>
</div>
))}
<button onClick={addEmail}>Add email</button>
<pre>{JSON.stringify(inviteEmails.filter(Boolean), null, 2)}</pre>
</div>
);
}
English is not my mother language so sorry for mistakes, i'm react beginner, my question:
I am at route /#/billingInfo/
when user clicks 'sender' button payers name will be senders name and same for 'receiver' button, but the problem is i'm pushing same url which is this same page, i get senders or receivers name in my input as i should but only when i refresh the page, my question is how to not refresh the page and get those in my input (need to render/forceUpdate < Form > or that specific input when user clicks those buttons
??)
my code:
const [form] = Form.useForm();
const query = window.location.toString().split("?")[1];
const urlParamss = new URLSearchParams(query);
const payer = new URLSearchParams(query).get("payer");
const bNameUrl = urlParamss.get("bName");
const PickUpName = urlParamss.get("pName");
const DeliveryName = urlParamss.get("dName");
let bName;
if (payer === "sender") {
bName = PickUpName;
} else if (payer === "receiver") {
bName = DeliveryName;
}
const senderPays = () => {
history.push(`/billingInfo/${customerId}?${query}&payer=sender`);
};
const receiverPays = () => {
history.push(`/billingInfo/${customerId}?${query}&payer=receiver`);
};
return (
<div>
<Form>
<div>
<Button onClick={senderPays}>sender</Button>
<Button onClick={receiverPays}>receiver</Button>
</div>
<Form.Item
label="payers name"
name="bName"
initialValue={bNameUrl || bName}
>
<Input type="string" />
</Form.Item>
</Form>
</div>
);
}
export default BillingInfo;
If the payer query parameter is the only difference in urls when the new routes are pushed, then you can "listen" for changes on that value in an useEffect hook.
Create a custom query parameter hook. From the React-Router docs Query Parameters.
const useQuery = () => {
return new URLSearchParams(useLocation().search);
};
Use this hook to get the query parameters.
const {
payer,
bName: bNameUrl,
dName: DeliveryName,
pName: PickUpName
} = useQuery();
Move bName into component state.
const [name, setName] = useState(bNameUrl || bName);
Use useEffect to handle any changes to payer and update name state.
useEffect(() => {
setName(payer === 'sender' ? PickUpName : DeliveryName);
}, [payer]);
Render name as the default input value (retains whatever form logic you may have). Use a react key on the form to help "re-initialize" it by indicating to react it's a "new" component and needs to be mounted. See Fully uncontrolled component with a key.
<Form key={name}>
<div>
<Button onClick={senderPays}>sender</Button>
<Button onClick={receiverPays}>receiver</Button>
</div>
<Form.Item
label="payers name"
name="bName"
initialValue={name}
>
<Input type="string" />
</Form.Item>
</Form>
I currently have a form that is auto generated based on the amount of "Activities" a current user has. Each activity has a name and a value. My goal is to submit these values to the backend to update them. Yet I can't figure out how to reference these inputs. I was trying to read about using "ref"s, yet they come back with {current:null}
Here is the auto generated list (excuse the placeholder text)
When I inspect console here is what I find from the ref:
Here is my code:
import React, { useEffect } from "react";
import { useDispatch, useStore } from "react-redux";
import { connect } from "react-redux";
import * as actions from "../store/actions/patientSide";
export function ActivityTemplates() {
const dispatch = useDispatch();
const store = useStore();
const ref = React.createRef();
useEffect(() => {
// Update the document title using the browser API
dispatch(actions.getTodaysActivityTemplates());
}, []);
const activities = store.getState().patientSide.todays_activities;
const listedStuff = activities.map((activity) => (
<div>
{activity.activity_name}
<label for="value"> Value: </label>
<input
type="number"
id="value"
defaultValue={activity.value}
min="0"
max="10"
></input>
</div>
));
const saveActivities = () => {
var inputs = ref;
console.log(inputs);
// Insert code to run the call to the backend
};
return (
<div>
<h1> Activity Templates</h1>
<form id="form" onSubmit={saveActivities()} ref={ref}>
{listedStuff}
<input type="submit" name="save" />
</form>
</div>
);
}
export default ActivityTemplates;
I am very new to React and JS and honestly have very little idea of what I'm doing, but if someone could point me in the right direction that would be awesome!
EDIT: After sleeping on it, I've realized I've just been trying to force react into my HTML. I believe I should instead use a React Form Hook, and do it properly from the ground up.
<form onSubmit={handleOnSubmit}>
<label>User Name</label>
<input type="text" name="username" /><br/>
<label>Password</label>
<input type="password" name="password" /><br/>
<input type="submit" value="Submit" />
</form>
const handleOnSubmit = (event) => {
const formData = new FormData(event.target);
const formDetails = {};
event.preventDefault();
for (let entry of formData.entries()) {
formDetails[entry[0]] = entry[1];
};
console.log("formDetails", formDetails);
}
You are getting the input fields value from "FormData" on onSubmit.
const saveActivities = (event) => {
event.preventDefault();
const data = new FormData(event.target);
// Insert code to run the call to the backend
}
You need to store the value
const [value, setValue] = React.useState();
Then give your input a onChange={e => setValue(e.target.value)}
I would change the id though
Is there any way of inject text of database into the input to be editable? not like placeholder.
Here is a picture as example.
In this case the text is placed as placeholder and obviously can not be editable without erase all.
Here is the code:
<div style={modalStyle} className={classes.paper}>
<form className="update-note create-note">
<input
className="input-edit-note"
name="title"
onChange={(e) => setTitle(e.currentTarget.value)}
value={title}
placeholder={props.title}
/>
<textarea
name="content"
onChange={(e) => setContent(e.currentTarget.value)}
value={content}
placeholder={props.content}
/>
<Fab onClick={submitNote}>
<AddCircleIcon />
</Fab>
</form>
</div>
Whatever you put in value={...} will be visible to edit in the textfield.
If it's props that you want to merge with component local data then I'd suggest doing so via useState and useEffect
Either this:
const Example = (props) => {
const [title, setTitle] = useState(props.title);
...
}
or like this
const Example = (props) => {
const [title, setTitle] = useState('');
useEffect(() => {
setTitle(props.title);
}, [props.title]);
...
}
And then use the value in the input tag
<input
...
value={title}
...
/>
Example component:
// When initiating this component, make sure to pass "title" as props
const Example = (props) => {
// Title is bound to state within this component
const [title, setTitle] = useState(props.title);
const _onChangeTitle = e => {
setTitle(e.target.value);
}
return (
<input
className="input-edit-note"
name="title"
onChange={_onChangeTitle}
value={title} // title will always be state title
placeholder={props.title} // props.title will always remain the same
/>
)
}
once you get your data from you db save it in your component state as i see you're already doing:
const MyComponent = (props) => {
const [title, setTitle] = useState(props.title);
...
}
then set theinput's value equal to the title data you recieve from your db.
At the onChange trigger the setTitle function that will update your title state.
<input
className="YourProfileInput"
placeholder="Insert Title"
value={title}
onChange={(e) => setTitle(e.currentTarget.value)}
/>
I am using a variable below.
var newInput = {
title: this.inputTitle.value,
entry: this.inputEntry.value
};
This is used by my input fields.
<input type="text" id="inputname" className="form-control" ref={el => this.inputTitle = el} />
<textarea id="inputage" ref={el => this.inputEntry = el} className="form-control" />
<button className="btn btn-info" onClick={this.sendthru}>Add</button>
Once I activate {this.sendthru} I want to clear my input fields. However, I am uncertain how to do so.
Also, as shown in this example, it was pointed out to me that I should use the ref property for input values. What I am unclear of is what exactly does it mean to have {el => this.inputEntry = el}. What is the significance of el in this situation?
Let me assume that you have done the 'this' binding of 'sendThru' function.
The below functions clears the input fields when the method is triggered.
sendThru() {
this.inputTitle.value = "";
this.inputEntry.value = "";
}
Refs can be written as inline function expression:
ref={el => this.inputTitle = el}
where el refers to the component.
When refs are written like above, React sees a different function object each time so on every update, ref will be called with null immediately before it's called with the component instance.
Read more about it here.
Declare value attribute for input tag (i.e value= {this.state.name}) and if you want to clear this input value you have to use this.setState({name : ''})
PFB working code for your reference :
<script type="text/babel">
var StateComponent = React.createClass({
resetName : function(event){
this.setState({
name : ''
});
},
render : function(){
return (
<div>
<input type="text" value= {this.state.name}/>
<button onClick={this.resetName}>Reset</button>
</div>
)
}
});
ReactDOM.render(<StateComponent/>, document.getElementById('app'));
</script>
I'm not really sure of the syntax {el => this.inputEntry = el}, but when clearing an input field you assign a ref like you mentioned.
<input type="text" ref="someName" />
Then in the onClick function after you've finished using the input value, just use...
this.refs.someName.value = '';
Edit
Actually the {el => this.inputEntry = el} is the same as this I believe. Maybe someone can correct me. The value for el must be getting passed in from somewhere, to act as the reference.
function (el) {
this.inputEntry = el;
}
I have a similar solution to #Satheesh using React hooks:
State initialization:
const [enteredText, setEnteredText] = useState('');
Input tag:
<input type="text" value={enteredText} (event handler, classNames, etc.) />
Inside the event handler function, after updating the object with data from input form, call:
setEnteredText('');
Note: This is described as 'two-way binding'
You can use input type="reset"
<form action="/action_page.php">
text: <input type="text" name="email" /><br />
<input type="reset" defaultValue="Reset" />
</form>
Now you can use the useRef hook to get some magic if you do not want to use the useState hook:
function MyComponent() {
const inputRef = useRef(null);
const onButtonClick = () => {
// #ts-ignore (us this comment if typescript raises an error)
inputRef.current.value = "";
};
return (
<>
<input ref={inputRef} type="text" />
<button onClick={onButtonClick}>Clear input</button>
</>
);
}
As I mentioned, if you are using useState that is the best way. I wanted to show you also this special approach.
Also after React v 16.8+ you have an ability to use hooks
import React, {useState} from 'react';
const ControlledInputs = () => {
const [firstName, setFirstName] = useState(false);
const handleSubmit = (e) => {
e.preventDefault();
if (firstName) {
console.log('firstName :>> ', firstName);
}
};
return (
<>
<form onSubmit={handleSubmit}>
<label htmlFor="firstName">Name: </label>
<input
type="text"
id="firstName"
name="firstName"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
/>
<button type="submit">add person</button>
</form>
</>
);
};
You can use useState:
import React, { useState } from 'react';
const [inputTitle, setInputTitle] = useState('');
then add value to your input component:
render() {
<input type="text" onChange={(e) => setInputTitle(e.target.value)}
value={inputTitle} />
<button onClick={handleSubmit} type="submit">Submit</button>
}
On your submit handler function:
setInputTitle('');
document.querySelector('input').defaultValue = '';
On the event of onClick
this.state={
title:''
}
sendthru=()=>{
document.getElementByid('inputname').value = '';
this.setState({
title:''
})
}
<input type="text" id="inputname" className="form-control" ref={el => this.inputTitle = el} />
<button className="btn btn-info" onClick={this.sendthru}>Add</button>
I used the defaultValue property, useRef, and onClick to achieve this.
let ref = useRef()
and then inside the return:
<input type="text" defaultValue="bacon" ref={ref} onClick={() => ref.current.value = ""} />
also if you want to use onChange for the input it wouldn't require any more configuration and you can just use it. If you want to have a dynamic defaultValue then you absolutely can, with useState.
A simple way to reset the input in React is by implementing the onBlur inside the input.
onBlur={cleanSearch}
ej:
const [search, setSearch] = useState('')
const handleSearch = ({target}) =>{
setSearch(target.value)
}
const cleanSearch = () =>setSearch('')
<input
placeholder="Searchโฆ"
inputProps={{ 'aria-label': 'search' }}
value={search}
onChange={handleSearch}
onBlur={cleanSearch}
/>
The way I cleared my form input values was to add an id to my form tag.
Then when I handleSubmit I call this.clearForm()
In the clearForm function I then use document.getElementById("myForm").reset();
import React, {Component } from 'react';
import './App.css';
import Button from './components/Button';
import Input from './components/Input';
class App extends Component {
state = {
item: "",
list: []
}
componentDidMount() {
this.clearForm();
}
handleFormSubmit = event => {
this.clearForm()
event.preventDefault()
const item = this.state.item
this.setState ({
list: [...this.state.list, item],
})
}
handleInputChange = event => {
this.setState ({
item: event.target.value
})
}
clearForm = () => {
document.getElementById("myForm").reset();
this.setState({
item: ""
})
}
render() {
return (
<form id="myForm">
<Input
name="textinfo"
onChange={this.handleInputChange}
value={this.state.item}
/>
<Button
onClick={this.handleFormSubmit}
> </Button>
</form>
);
}
}
export default App;