In the following code, how comes the const password uses .querySelector in the authorize() function? I am a bit confused because:
JSX isn't strictly Javascript, so how come you can even use .querySelector?
And secondly, in JavaScript, you'd just select input as this would represent the password input box, but in this case input[type="password"] has been selected. Is this because in HTML the input boxes are not named so you have to differentiate them?
If authorize() uses e.target, why is querySelector needed? won't authorize already realize the password input field is what is triggering the function? Or am I wrong and instead, the 'e' triggering authorize() is the form and so similar to document.querySelector, you then need to select the input field itself as this is a subtype of the form?
import React from 'react';
import ReactDOM from 'react-dom';
class Contact extends React.Component {
constructor(props) {
super(props);
this.state = {
password: 'swordfish',
authorized: false
};
this.authorize = this.authorize.bind(this);
}
authorize(e) {
const password = **e.target.querySelector(
'input[type="password"]').value;**
const auth = (password == this.state.password);
this.setState({
authorized: auth
});
}
render() {
let login = (
**<form action="#" onSubmit={this.authorize}>**
<input type="password" placeholder="password"></input>
<input type="submit"></input>
</form>
);
let contactInfo = (
<ul>
<li>
client#example.com
</li>
<li>
555.555.5555
</li>
</ul>
);
return (
<div id="authorization">
<h1>{this.state.authorized ? 'Contact' : 'Enter the Password'}</h1>
<h1>{this.state.authorized ? contactInfo : login}</h1>
</div>
);
}
}
ReactDOM.render(
<Contact />,
document.getElementById('app')
);
Thanks!
JSX is extended syntax for JS. Adding more stuff doesn’t stop you using existing stuff.
There is no need to use a selector that specific in that piece of code. It does not harm and can aid readability.
The target of the submit event is the form, not the input. If you need to read the input, then you have to select it somehow. querySelector is a pretty common approach.
That said, this is not an idiomatic approach to writing React applications. See the documentation on forms for the accepted React approach.
Usually, you want to have controlled components in React and keep the input value inside state and let React be the single source of truth, not leave it to the DOM to decide what's what. A simple example, using hooks, would be:
import React from 'react';
const SimpleExample = () => {
// use a piece of state to track and hold the input value
const [inputVal, setInputVal] = useState("");
// change state on input change
const handleChange = event => setInputVal(event.target.value);
// react also handles submit
const handleSubmit = event => {
// prevent default browser behaviour
event.preventDefault();
// you can send values held inside state to the back-end here
console.log("Submitting...", inputVal)
}
return (
<form onSubmit={event => handleSubmit(event)}>
<input
type="password"
// the value of the input is held by React state
value={inputVal}
// change state something is being typed inside input
onChange={event => handleChange(event)}
/>
<button>Submit</button>
</form>
)
}
Related
I'm new to svelte and I'm trying to get a sense for best practices. I want to create a dynamic form component I can use throughout an app. Each instance of the form will have the same styling - but each form will be tied to different input values in the store, will have different submit logic, etc.
Here's a simple example (that doesn't work):
App.svelte
<script>
import store from "./store";
import Form from "./Form.svelte";
const { inputValueA, inputValueB } = store;
const handleSubmitA = () => {
alert($inputValueA);
};
const handleSubmitB = () => {
alert($inputValueB);
};
</script>
<div>
<Form
inputValue={$inputValueA}
labelText="form input a"
handleSubmit={handleSubmitA}
/>
<Form
inputValue={$inputValueB}
labelText="form input b"
handleSubmit={handleSubmitB}
/>
</div>
Form.svelte
<script>
export let handleSubmit = () => null;
export let inputValue = "";
export let labelText = "";
</script>
<form>
<label for="input">{labelText}</label>
<input id="input" type="text" value={inputValue} />
<button on:click|preventDefault={handleSubmit}>submit</button>
</form>
This example doesn't work, I don't believe the inputValue prop is bound to the text input correctly. Any folks out there with svelte experience willing to share how you'd approach this?
Here's a sandbox:
https://codesandbox.io/s/svelte-form-component-ujnwki
Let me know what you suggest! Thanks.
Not really sure what is exactly is your question but here is the fix for your example to make it work:
you have to bind the value to your component to be able to change it form the child
bind:inputValue={$inputValueA}
and bind the value to the input itself to make its value reactive
bind:value={inputValue}
I want to submit two forms from a single button in next.js without using document.getElementById.
I am using two form tags and fetching their data in two different objects and I want them to be submitted at once with a single button.
I've tried doing this by document.getElementById.Submit() but it throws an error that document is not defined.
Similar question has been asked on SO but the solution offered is by using JQuery which I can't use.
const submitForms = function () {
useEffect(()=>
{
document.getElementById("form1").submit();
document.getElementById("form2").submit();
})
};
Please tell me where am I going wrong?
The basic principle is that you store your form data in state, update the state when the form information changes, and then when the button is clicked, submit the state data to the server endpoint that processes that data.
In this working example we use an object as the state, and assign each form an id (see data attributes). We attach listeners to the forms so that when any of the inputs change we can capture their events as they bubble up the DOM (event delegation).
In the handleChange function we take the id from dataset of the form (currentTarget), and the name and value from the element that was changed (target) - more on the key differences here - and then update the state using that information.
handleClick then stringifies the state, at which point you can submit it to the server endpoint.
It may benefit you to revisit how React works. It has its own way of updating the DOM which means you shouldn't be using any native DOM methods.
const { useEffect, useState } = React;
function Example() {
// Set the state
const [ forms, setForms ] = useState({});
function handleChange(e) {
// Destructure the id from the dataset of the form
const { dataset: { id } } = e.currentTarget;
// Destructure the name and value from the
// element that changed
const { name, value } = e.target;
// Update the state. We preserve the existing
// state data using the spread syntax, and set the new
// value of the property that has the id as key.
// That new value is an object representing the old
// value data (again using the spread syntax), and
// updating the property using the element name and value.
setForms({
...forms,
[id]: { ...forms[id], [name]: value }
});
}
function handleClick() {
// Stringify the data. You can now use
// `fetch` or something like Axios to send the
// data to the server
console.log(JSON.stringify(forms));
}
return (
<div>
<form data-id="formone" onChange={handleChange}>
<fieldset>
<legend>Form 1</legend>
Name: <input name="name" />
<br/>
Age: <input name="age" />
</fieldset>
</form>
<form data-id="formtwo" onChange={handleChange}>
<fieldset>
<legend>Form 2</legend>
Role: <input name="role" />
</fieldset>
</form>
<button
type="button"
onClick={handleClick}
>Submit all form data
</button>
</div>
);
};
ReactDOM.render(
<Example />,
document.getElementById('react')
);
form, button { margin-top: 1em; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Additional documentation
Spread syntax
Destructuring assignment
you can use a function and when form submitted, use in first line of your function:
e.preventDefault()
And when click on another button, call this function and when form is submitted also run this function
Don't use DOM function when work with ReactJs or NextJs
// App.js
const [currentContent, setCurrentContent] = useState('')
const openNote = (id) => {
notes.forEach(note => note.id == id && setCurrentContent(note.content))
}
Part of return:
<TextRegion className="center" content={currentContent}/>
I am passing content as a prop via a state seen in the code block above. This seems to work fine.
// TextRegion.js
import React, {useState} from 'react'
const TextRegion = ({ content }) => {
const [areaText, setAreaText] = useState(content)
console.log(areaText)
return (
<div className="form-div">
<form className="form">
<textarea className="content form-control" type="text" value={content}/>
<button style={{'float': 'right'}} className="btn btn-primary mt-2 mr-2" type='submit'>Save</button>
</form>
</div>
)
}
export default TextRegion;
The issue arises when I attempt to set the content prop to the default state of areaText. Content is of type string, and prints to console just fine. Although, when trying to print areaText, an empty string is returned. This is baffling to me, any explanations? Thanks.
The initial value for useState is used once when the component is mounted. In your TextRegion component, areaText is set to the value of the content prop when TextRegion is mounted. Since currentContent is initialised as an empty string, so is areaText.
If you change the value of content prop while the TextRegion is mounted, the useState hook controlling areaText will just ignore the new value, because it has already initialised the value for areaText.
One issue I found is in your textRegion.js you are using form and the button is type of submit so that will reload your page. always use e.preventDefault on submit method in react applications.
You can pass both currentContent and setCurrentContent via pros and use that state directly rather than creating a new state.
Use two way binding in react.
Example
<textarea
className="content form-control" type="text"
value={areaText}
onChage={(e)=> setAreaText(e.target.value)}
/>
Suppose I have a settings screen on my React app, with three settings
enable thingie
use doohickey
activate gizmo
What's the best way to handle all the onchange events from all these controls? Here's what I have at the minute
import React from 'react';
import Modal from 'react-bootstrap-modal';
export default class Settings extends React.Component {
constructor (props) {
super(props)
this.state = {
open: false
}
this.handleThingieChange = this.handleThingieChange.bind(this);
this.handleDoohickeyChange = this.handleDoohickeyChange.bind(this);
this.handleGizmoChange = this.handleGizmoChange.bind(this);
}
handleThingieChange(e) {
this.props.onThingieChange(e.target.value)
}
handleDoohickeyChange(e) {
this.props.onDoohickeyChange(e.target.value);
}
handleGizmoChange(e) {
this.props.onGizmoChange(e.target.checked);
}
render() {
return (
<div>
<label>enable thingie</label>
<input type="checkbox" checked={this.props.thingie} onChange={this.handleThingieChange} />
<label>use doohickey</label>
<input type="checkbox" checked={this.props.doohickey} onChange={this.handleDoohickeyChange} />
<label>activate gizmo</label>
<input type="checkbox" checked={this.props.gizmo} onChange={this.handleGizmoChange} />
</div>
)
}
}
And in my <App> component:
thingieChange(thingie){
this.setState({ thingie: thingie })
}
doohickeyChange(doohickey){
this.setState({ doohickey: doohickey })
}
gizmoChange(gizmo){
this.setState({ gizmo: gizmo })
}
// render method is most important
// render method returns JSX template
render() {
return (
<form>
<h2>headerooney</h2>
<Settings
onThingieChange={this.thingieChange.bind(this)}
onDoohickeyChange={this.doohickeyChange.bind(this)}
onGizmoChange={this.gizmoChange.bind(this)} />
</form>
);
}
My app could potentially have 20 or 30 settings, it seems like there must be a better way to do this than by repeating this pattern 20-30 times?
I'm new to react, if I was building this with jquery I'd do something like this:
<div>
<label>enable thingie</label>
<input type="checkbox" data-property='thingie' />
<label>use doohickey</label>
<input type="checkbox" data-property='doohickey' />
<label>activate gizmo</label>
<input type="checkbox" data-property='gizmo' />
</div>
with a generic click handler like this
$(function(){
$.each(options, function(k, v) {
$("[data-property='" + k + "']")[0].checked = v;
});
$(document).on("click", "input[type='checkbox']", function(){
var propertyName = $(this).data("property");
options[propertyName] = $(this)[0].checked;
console.log(options);
})
});
How do I do something equivalent in react? I don't want to spend two hours writing a handler function for every single setting
If all of your controls are just checkboxes then why not create a single component for a setting and then have a Settings component that lists them all.
You could provide the name of the setting as a property for the component so that it knows what it was updating. You could pass a function from the Settings component to the Setting component that provided the means to update state.
One more complex option for addressing this would be to use Redux but it is probably not necessary for something this straightforward (although it would provide plenty of benefit in terms of simplifying testing, etc).
I would suggest though that as soon as you seen any kind of repeating pattern that you try to abstract it to a reusable component - it will definitely save you time in the long run.
I'm learning React and as a learning exercise am trying to do a very basic page where there is a form and you put text in an input box, you click submit and the header changes to what you entered. Here is my code so far:
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor() {
super();
this.state = {header: 'yeaheheh'}
}
changeHeader(e) {
let newHeader = document.getElementById('input').value();
e.preventDefault();
console.log('submitted');
this.setState(newHeader);
}
render() {
return (
<div>
<h1>{this.state.header}</h1>
<form onSubmit={this.changeHeader.bind(this)} className="change-header-form">
<input id="input" type="text" placeholder="Enter Text Here" />
<input type="submit" value="Submit" />
</form>
</div>
);
}
}
export default App;
At first, when I clicked submit, nothing happened and I got an error in the console that says
Uncaught TypeError: Cannot read property 'setState' of null
I then realized I needed to bind the changeHeader function to this which I changed so before I had:
<form onSubmit={this.changeHeader}...
changed it to
<form onSubmit={this.changeHeader.bind(this)}...
After doing this, the error cleared but my header is still not updating.I read that there has been strong suggestions against changing state via setState is bad practice because calling setState() again could potentially alter the changed state. setState is also an asynchronous operation which would also explain why my header isn't changing.
With all that said, then what would be the best way to handle this? From what I understand, props wouldn't make sense either since those values are stored directly in your component and aren't parameters that can't be dynamically updated. I'm having a hard time understanding the relationship between these different data types and how they are handled in the DOM.
You are setting state incorrectly.
More over to get the data from input fields you can either use controlled input elements(via states) or uncontrolled input elements via "ref" which I have used in below example.
In controlled input element you store the value of input element in state and changes to that value is done by calling onChange method and then setting the state via this.setState({}).
Calling setState causes re-rendering to happen and dom gets the updated data based on new state.
Btw "refs" gets you the direct access to dom elements, in similar way $() was used in jquery and should be avoided if possible because it will lead to very hard to manage and predict dom changes.
Also there are cases where use of "refs" is recommended
There are a few good use cases for refs:
Managing focus, text selection, or media playback.
Triggering imperative animations.
Integrating with third-party DOM libraries.
class App extends React.Component {
constructor() {
super();
this.state = {header: 'yeaheheh'};
}
changeHeader = (e) => {
e.preventDefault();
let newHeader = this.textInput.value;
console.log('submitted');
this.setState({header : newHeader});
}
render() {
return (
<div>
<h1>{this.state.header}</h1>
<form onSubmit={this.changeHeader} className="change-header-form">
<input id="input" ref={(input) => { this.textInput = input; }} type="text" placeholder="Enter Text Here" />
<input type="submit" value="Submit" />
</form>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('test'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="test">
</div>
Replace this.setState(newHeader); with this.setState({header: newHeader});.
Take a look at this article in the react docs: https://facebook.github.io/react/docs/forms.html#controlled-components.
Basically what you want to do is create another handler for the input. This will be called every time there is a change to the input field and a property in your state will be updated. Then, when you submit the form you can take that new property and "merge" it using setState to become the new header.
JS Bin
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
header: 'yeaheheh',
next: ''
}
this.changeHeader = this.changeHeader.bind(this);
this.updateNext = this.updateNext.bind(this);
}
changeHeader(e) {
e.preventDefault();
this.setState({
header: this.state.next
});
}
updateNext(e) {
this.setState({
next: e.target.value
});
}
render() {
return (
<div>
<h1>{this.state.header}</h1>
<form onSubmit={this.changeHeader} className="change-header-form">
<input id="input" type="text" placeholder="Enter Text Here" onChange={this.updateNext} />
<input type="submit" value="Submit" />
</form>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
Maybe this bin will provide a little better context at what I'm trying to describe.
There's the small bug in your code preventing it from working (this.setState(newHeader) -> this.setState({header: newHeader});), but the thing is that your code is not idiomatic for React.
You are supposed to use controlled components instead of grabbing the values from the form's inputs on submit, as you would do with jQuery.
"Controlled component" is a silly name for the pattern where an input's state is mapped to the application state, so an input itself behaves as if it would be kinda "stateless". In your case, you need to have separate component state member for every text input you've got. Input control should look like this:
<input value={ this.state.inputValue }
onChange={ e => this.setState({ inputValue : e.target.value }) }
/>
Now it's bound to your inputValue state member, so you can just take it from the state at any moment you need. On form's submit handler, in your case.
That's it. Your code must be fixed accordingly. Refer to the "controlled components" manual for further details, it's the really important React concept.
You should modified your function like this..
constructor(props) {
super(props);
_that = this;
}
changeHeader = (e) => {
e.preventDefault();
let newHeader = this.textInput.value;
console.log('submitted');
_that.setState({header : newHeader});
}