Submit form on radio button select in React - javascript

I currently have several radio button forms which are submitting data to my backend nicely when i hit the return key. However I want each form to submit and move to the next component/form as soon as a radio button is selected. Is there a way to submit a form and move to the next component on radio button selection?
Can onselect be used here, and if so how do i go about using it?
Here's my code:
export class ChoiceOne extends React.Component {
constructor(props) {
super(props);
this.state = {
value: null
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange = (e) => {
const colourType = e.target.value;
this.setState({
colourType
});
};
handleSubmit(event) {
event.preventDefault();
const typeOneItem = {
time: new Date().toLocaleString("en-gb"),
typeOne: this.state.colourType
};
firebase.writeTo(`${firebase.getCurrentUser().uid}/typeOne`, typeOneItem);
this.props.onChosen(1);
}
render() {
const colour = ['Red', 'Blue', 'Green'];
return (
<
div className = "type1" >
<
div className = "content" >
<
form onSubmit = {
this.handleSubmit
} >
{
colour.map((colour, index) =>
<
label key = {
index
} > {
colour
} <
input value = {
colour.toUpperCase()
}
checked = {
this.state.colourType === colour.toUpperCase()
}
onChange = {
this.handleChange
}
type = "radio" / >
<
/label>
)
} <
input type = "submit"
hidden / >
<
/form> < /
div > <
/div>

call your handleSubmit function from the handleChange function
for example in the `handleChange function that is called every time a radio button is selected
handleChange = (e) => {
const colourType = e.target.value;
this.setState({
colourType
});
if(//check if you are ready to submit) {
this.handleSubmit();
e.preventDefault();
}
};
handleSubmit() {
const typeOneItem = {
time: new Date().toLocaleString("en-gb"),
typeOne: this.state.colourType
};
firebase.writeTo(`${firebase.getCurrentUser().uid}/typeOne`, typeOneItem);
this.props.onChosen(1);
}
you can use onselect but since you already have a onchange here it would be more efficient to use that!

Either you can do conditional rendering, by rendering the specific component on after successfull setState, for that you have to create the components you needed and you can use the props call back to set the data in the parent. Or you can keep an iterartor and do multiple returns inside the same component.

Related

Creating a custom input field with Web Components without outside accessability

I want to create a custom input with the Shadow DOM
class TextBox extends HTMLElement {
constructor() {
super();
var shadow = this.attachShadow({ mode: 'open' });
let textbox = document.createElement("input");
shadow.appendChild(textbox);
textbox.addEventListener("change", validate);
function validate(event) {
console.log("input can be validated");
}
}
get value() {
console.log("get");
let textbox = this.shadowRoot.querySelector("input");
return textbox.value;
}
set value(newValue) {
console.log("set");
let textbox = this.shadowRoot.querySelector("input");
textbox.value = newValue;
}
}
customElements.define('test-textbox', TextBox);
It should be possible to change the value of the displayed textbox via js. If I change the .value property of the textbox the setter of value don't get called? Am i missing something?
Later on I want to include the textbox via a template in my solution and be able to set the value of the textbox via textbox.value ="Peter"
The internal <input> field dispatches the input event every time its value changes. This event can be captured either in your component or by the code that uses your component.
The change event only happens in certain situations so the input event is a better choice.
The code below shows how the component listens for the input event and so does the external code.
function validate(event) {
console.log("input can be validated");
}
class TextBox extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = `
<style>
input {
width: 300px;
}
</style>
`;
const textbox = document.createElement("input");
shadow.appendChild(textbox);
textbox.addEventListener("input", validate);
textbox.focus();
}
get value() {
console.log("get");
let textbox = this.shadowRoot.querySelector("input");
return textbox.value;
}
set value(newValue) {
console.log("set");
let textbox = this.shadowRoot.querySelector("input");
textbox.value = newValue;
}
}
customElements.define('test-textbox', TextBox);
const el = document.querySelector('test-textbox');
el.addEventListener("input", (evt) => {
console.log('input event from the outside.');
});
<test-textbox></test-textbox>

How to modify this code so that changes in the dropdown of the child page are only reflected in the parent after apply & not on onchange? - ReactJS

This is my Code for Parent. As you can see the child is only opened when this.state.isFilterPageOpen is true. Basically I have a filter button in my parent page, clicking on which the this.state.isFilterPageOpen variable is set true and filter overlay page opens.
{this.state.isFilterPageOpen &&
<FilterPage
data={this.state.dataDefault}
applyFilter={this.applyFilter}
applyDefault={this.applyDefault}
filtered={this.state.filteredChild}
onceSubmitted={this.state.filterOnceSubmitted}
closeFilterChildPage={this.closeFilterChildPage}
/>}
This is my relevant pieces of code from the Filter Page (which is displayed as a popup/overlay over the parent page). I also used Lodash here. I have multiple Select dropdowns. I am showing just two of them.
class FilterPage extends Component {
constructor(props) {
super(props);
this.state = {
data: this.props.data,
filtered: this.props.filtered,
filteredPush: this.props.filtered,
onceSubmitted: this.props.onceSubmitted,
};
}
render() {
// lines of code //
const indexSource = _.findIndex(filteredPush, (o) => { return o.id === 'Source'; });
const indexDestination = _.findIndex(filteredPush, (o) => { return o.id === 'Destination'; });
return (
// lines of code
<div className='closefilterview-div'>
<input
type='image'
className='closefilterpage'
src={close}
alt="close"
onClick={() => {
this.props.closeFilterChildPage();
}}
/>
</div>
<ButtonComponent
text='Apply Filter'
className='button filter-apply'
width='100'
display='inline-block'
onClick={() => {
this.setState({
onceSubmitted: true,
filtered: this.state.filteredPush
}, () => {
this.props.applyFilter(this.state.filtered);
});
}}
/>// button for apply
// other lines of code
<div className='dropdown'>Source
<Select
placeholder='ALL'
onChange={(entry) => {
let valuesSource = [];
for (let i = 0; i < entry.length; i = i + 1) {
valuesSource.push(entry[i].value);
}
if(indexSource === -1) {
filteredPush.push({ id: 'Source', value: valuesSource });
} else {
filteredPush[indexSource].value = valuesSource;
}
this.setState({ filteredPush }); // setstate here causing the issue
}}
value={(indexSource === -1) ? [] : filteredPush[indexSource].value}
multi={true}
options={this.getOptions('Source')}
/>
</div>
<div className='dropdown'>Destination
<Select
placeholder='ALL'
onChange={(entry) => {
let valuesDest = [];
for (let i = 0; i < entry.length; i = i + 1) {
valuesDest.push(entry[i].value);
}
if(indexDestination === -1) {
filteredPush.push({ id: 'Destination', value: valuesDest });
} else {
filteredPush[indexDestination].value = valuesDest;
}
this.setState({ filteredPush }); // setstate here causing the issue
}}
value={(indexDestination === -1) ? [] : filteredPush[indexDestination].value}
multi={true}
options={this.getOptions('Destination')}
/>
</div>
);// end of return
// lines of code
}
Now the issue is the filteredPush and filtered state variable is getting updated on parent page whenever I changes any multi select dropdowns. I have a huge issue when I press on the close button of the page.
Now suppose I select 2 items from destination dropdown and click apply button. Then everything is fine.
Next time I open the filter page again. All the previous selected dropdowns are shown in the placeholders of the respective dropdowns.
Now the issue is when I select any destination (now 3 destination together) but instead of clicking apply button I click the close button to close the filter overlay filter. Then also my state variables filteredPush and also filtered is getting updated in the parent as they both get data from the props. I have to code in such a way such that filteredPush and also filtered are updated on applying and not on onchange of respective dropdowns.
How can I achieve this?
You shouldn't mutate the object passed from the parent component like this filteredPush.push({... }). Instead, clone the object use _.clone(obj) or _.cloneDeep() if the filteredPush is nested, to a new obj then it will not be affected on the parent component. The change in onChange function:
<Select
placeholder='ALL'
onChange={(entry) => {
let valuesSource = [];
for (let i = 0; i < entry.length; i = i + 1) {
valuesSource.push(entry[i].value);
}
let newFilteredPush = _.cloneDeep(filteredPush)
if(indexSource === -1) {
newFilteredPush.push({ id: 'Source', value: valuesSource });
} else {
newFilteredPush[indexSource].value = valuesSource;
}
this.setState({ filteredPush: newFilteredPush });
}}
...
/>
Firstly welcome to StackOverflow.
To answer your question I believe the problem lies in <Select onChange={...} /> component. In your code I see you editing the state onChange. Instead, you should only be pushing values to a global(this) variable in the onChange={fn}. And setState only on the ButtonCompenents' onClick={fn}.
So firstly initialize the newFilteredPush in class as an empty array. And then the modified Select would look like -
<Select
placeholder='ALL'
onChange={(entry) => {
let valuesSource = [];
for (let i = 0; i < entry.length; i = i + 1) {
valuesSource.push(entry[i].value);
}
let newFilteredPush = _.cloneDeep(filteredPush)
if(indexSource === -1) {
this.newFilteredPush.push({ id: 'Source', value: valuesSource });
} else {
this.newFilteredPush[indexSource].value = valuesSource;
}
}}
...
/>
And your ButtonComponent should look like
<ButtonComponent
text='Apply Filter'
className='button filter-apply'
width='100'
display='inline-block'
onClick={() => {
this.setState({
onceSubmitted: true,
filtered: this.newFilteredPush
}, () => {
this.props.applyFilter(this.state.filtered);
});
}}
/>
Hope this helps.

How to programmatically fill input elements built with React?

I'm tasked with crawling website built with React. I'm trying to fill in input fields and submitting the form using javascript injects to the page (either selenium or webview in mobile). This works like a charm on every other site + technology but React seems to be a real pain.
so here is a sample code
var email = document.getElementById( 'email' );
email.value = 'example#mail.com';
I the value changes on the DOM input element, but the React does not trigger the change event.
I've been trying plethora of different ways to get the React to update the state.
var event = new Event('change', { bubbles: true });
email.dispatchEvent( event );
no avail
var event = new Event('input', { bubbles: true });
email.dispatchEvent( event );
not working
email.onChange( event );
not working
I cannot believe interacting with React has been made so difficult. I would greatly appreciate any help.
Thank you
This accepted solution appears not to work in React > 15.6 (including React 16) as a result of changes to de-dupe input and change events.
You can see the React discussion here: https://github.com/facebook/react/issues/10135
And the suggested workaround here:
https://github.com/facebook/react/issues/10135#issuecomment-314441175
Reproduced here for convenience:
Instead of
input.value = 'foo';
input.dispatchEvent(new Event('input', {bubbles: true}));
You would use
function setNativeValue(element, value) {
const valueSetter = Object.getOwnPropertyDescriptor(element, 'value').set;
const prototype = Object.getPrototypeOf(element);
const prototypeValueSetter = Object.getOwnPropertyDescriptor(prototype, 'value').set;
if (valueSetter && valueSetter !== prototypeValueSetter) {
prototypeValueSetter.call(element, value);
} else {
valueSetter.call(element, value);
}
}
and then
setNativeValue(input, 'foo');
input.dispatchEvent(new Event('input', { bubbles: true }));
React is listening for the input event of text fields.
You can change the value and manually trigger an input event, and react's onChange handler will trigger:
class Form extends React.Component {
constructor(props) {
super(props)
this.state = {value: ''}
}
handleChange(e) {
this.setState({value: e.target.value})
console.log('State updated to ', e.target.value);
}
render() {
return (
<div>
<input
id='textfield'
value={this.state.value}
onChange={this.handleChange.bind(this)}
/>
<p>{this.state.value}</p>
</div>
)
}
}
ReactDOM.render(
<Form />,
document.getElementById('app')
)
document.getElementById('textfield').value = 'foo'
const event = new Event('input', { bubbles: true })
document.getElementById('textfield').dispatchEvent(event)
<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='app'></div>
Here is the cleanest possible solution for inputs, selects, checkboxes, etc. (works not only for react inputs)
/**
* See [Modify React Component's State using jQuery/Plain Javascript from Chrome Extension](https://stackoverflow.com/q/41166005)
* See https://github.com/facebook/react/issues/11488#issuecomment-347775628
* See [How to programmatically fill input elements built with React?](https://stackoverflow.com/q/40894637)
* See https://github.com/facebook/react/issues/10135#issuecomment-401496776
*
* #param {HTMLInputElement | HTMLSelectElement} el
* #param {string} value
*/
function setNativeValue(el, value) {
const previousValue = el.value;
if (el.type === 'checkbox' || el.type === 'radio') {
if ((!!value && !el.checked) || (!!!value && el.checked)) {
el.click();
}
} else el.value = value;
const tracker = el._valueTracker;
if (tracker) {
tracker.setValue(previousValue);
}
// 'change' instead of 'input', see https://github.com/facebook/react/issues/11488#issuecomment-381590324
el.dispatchEvent(new Event('change', { bubbles: true }));
}
Usage:
setNativeValue(document.getElementById('name'), 'Your name');
document.getElementById('radio').click(); // or setNativeValue(document.getElementById('radio'), true)
document.getElementById('checkbox').click(); // or setNativeValue(document.getElementById('checkbox'), true)
I noticed the input element had some property with a name along the lines of __reactEventHandlers$..., which had some functions including an onChange.
This worked for finding that function and triggering it
let getReactEventHandlers = (element) => {
// the name of the attribute changes, so we find it using a match.
// It's something like `element.__reactEventHandlers$...`
let reactEventHandlersName = Object.keys(element)
.filter(key => key.match('reactEventHandler'));
return element[reactEventHandlersName];
}
let triggerReactOnChangeEvent = (element) => {
let ev = new Event('change');
// workaround to set the event target, because `ev.target = element` doesn't work
Object.defineProperty(ev, 'target', {writable: false, value: element});
getReactEventHandlers(element).onChange(ev);
}
input.value = "some value";
triggerReactOnChangeEvent(input);
Without element ids:
export default function SomeComponent() {
const inputRef = useRef();
const [address, setAddress] = useState("");
const onAddressChange = (e) => {
setAddress(e.target.value);
}
const setAddressProgrammatically = (newValue) => {
const event = new Event('change', { bubbles: true });
const input = inputRef.current;
if (input) {
setAddress(newValue);
input.value = newValue;
input.dispatchEvent(event);
}
}
return (
...
<input ref={inputRef} type="text" value={address} onChange={onAddressChange}/>
...
);
}
React 17 works with fibers:
function findReact(dom) {
let key = Object.keys(dom).find(key => key.startsWith("__reactFiber$"));
let internalInstance = dom[key];
if (internalInstance == null) return "internalInstance is null: " + key;
if (internalInstance.return) { // react 16+
return internalInstance._debugOwner
? internalInstance._debugOwner.stateNode
: internalInstance.return.stateNode;
} else { // react <16
return internalInstance._currentElement._owner._instance;
}
}
then:
findReact(domElement).onChangeWrapper("New value");
the domElement in this is the tr with the data-param-name of the field you are trying to change:
var domElement = ?.querySelectorAll('tr[data-param-name="<my field name>"]')

Change the cursor position in a textarea with React

I have a textarea in React that I want to turn into a "notepad". Which means I want the "tab" key to indent instead of unfocus. I looked at this answer, but I can't get it to work with React. Here is my code:
handleKeyDown(event) {
if (event.keyCode === 9) { // tab was pressed
event.preventDefault();
var val = this.state.scriptString,
start = event.target.selectionStart,
end = event.target.selectionEnd;
this.setState({"scriptString": val.substring(0, start) + '\t' + val.substring(end)});
// This line doesn't work. The caret position is always at the end of the line
this.refs.input.selectionStart = this.refs.input.selectionEnd = start + 1;
}
}
onScriptChange(event) {
this.setState({scriptString: event.target.value});
}
render() {
return (
<textarea rows="30" cols="100"
ref="input"
onKeyDown={this.handleKeyDown.bind(this)}
onChange={this.onScriptChange.bind(this)}
value={this.state.scriptString}/>
)
}
When I run this code, even if I press the "tab" key in the middle of the string, my cursor always appears at the end of the string instead. Anyone knows how to correctly set the cursor position?
You have to change the cursor position after the state has been updated(setState() does not immediately mutate this.state)
In order to do that, you have to wrap this.refs.input.selectionStart = this.refs.input.selectionEnd = start + 1; in a function and pass it as the second argument to setState (callback).
handleKeyDown(event) {
if (event.keyCode === 9) { // tab was pressed
event.preventDefault();
var val = this.state.scriptString,
start = event.target.selectionStart,
end = event.target.selectionEnd;
this.setState(
{
"scriptString": val.substring(0, start) + '\t' + val.substring(end)
},
() => {
this.refs.input.selectionStart = this.refs.input.selectionEnd = start + 1
});
}
}
jsfiddle
For anyone looking for a quick React Hooks (16.8+) cursor position example:
import React, { useRef } from 'react';
export default () => {
const textareaRef = useRef();
const cursorPosition = 0;
return <textarea
ref={textareaRef}
onBlur={() => textareaRef.current.setSelectionRange(cursorPosition, cursorPosition)}
/>
}
In this example, setSelectionRange is used to set the cursor position to the value of cursorPosition when the input is no longer focused.
For more information about useRef, you can refer to React's official doc's Hook Part.
Here's a solution in a hooks-style architecture. My recommendation is to change the textarea value and selectionStart immediately on tab insertion.
import React, { useRef } from "react"
const CodeTextArea = ({ onChange, value, error }) => {
const textArea = useRef()
return (
<textarea
ref={textArea}
onKeyDown={e => {
if (e.key === "Tab") {
e.preventDefault()
const { selectionStart, selectionEnd } = e.target
const newValue =
value.substring(0, selectionStart) +
" " +
value.substring(selectionEnd)
onChange(newValue)
if (textArea.current) {
textArea.current.value = newValue
textArea.current.selectionStart = textArea.current.selectionEnd =
selectionStart + 2
}
}
}}
onChange={e => onChange(e.target.value)}
value={value}
/>
)
}
In React 15 best option is something like that:
class CursorForm extends Component {
constructor(props) {
super(props);
this.state = {value: ''};
}
handleChange = event => {
// Custom set cursor on zero text position in input text field
event.target.selectionStart = 0
event.target.selectionEnd = 0
this.setState({value: event.target.value})
}
render () {
return (
<form>
<input type="text" value={this.state.value} onChange={this.handleChange} />
</form>
)
}
}
You can get full control of cursor position by event.target.selectionStart and event.target.selectionEnd values without any access to real DOM tree.

Two way data binding using div

I am working with reactjs with redux.
i have created an editable div instead of input textfield but unable to receive the value.
So, in input textfield. There is a event named onChange which let you access the value type in input field.
For example -
handlechange(e){
console.log(e.target.value); //get the value of textbox then i further save it in state
}
render()
{
return (
<input
onChange={this.handleChange.bind(this)}
value={this.state.msgText}
</>
)}
But I am using the editable div for same like this
<div
role="textbox"
ref={function(e){if(e != null) e.contentEditable=true;}}
title="Type the text"
//onChange={this.handleChange.bind(this)}
onKeyUp={this.handleKeyUp.bind(this)}
>
{this.state.msgText}
</div>
So , in handleKeyUp function
handleKeyUp(e){
var t = this;
console.log('test');
console.log(e);
console.log(e.target.value); // I have read ,i can only receive the keycode here,cannot receive value
console.log(this.state.msgText); //So i should receive the value here but i am not
if(e.which == 13) {
e.preventDefault();
//reset the state for clear the div
t.setState({
msgText: ""
});
}
}
Once way of doing this is adding id on div like this -
<div
id={"fc-"+ this.props.thread.uid + "-textbox"}
role="textbox"
className="ifc-chat-window-textbox-input"
ref={function(e){if(e != null) e.contentEditable=true;}}
title="Type the text"
//onChange={this.handleChange.bind(this)}
//onKeyUp={this.handleKeyUp.bind(this)}
>
{this.state.msgText}
</div>
Then in componentDidMount function
componentDidMount(){
var t = this;
var node = document.getElementById("fc-"+ this.props.thread.uid + "-textbox");
var value = node.textContent; // I receive the value here
node.onkeypress = function(event){
t.setState({
msgText: node.textContent
}); });
if(event.which == 13) {
event.preventDefault();
t.sendMsgObject(value , t.props.thread.uid, t.props.thread.name, t.props.thread.color, t.props.actions, t.props.user);
//reset the state for clear input field
t.setState({
msgText: ""
});
}
All this works fine, but i dont think that is how things works in react. I am looking do to this without using id to div.
have u tried something like that ?
var handleChange = function(event){
this.setState({html: event.target.value});
}.bind(this);
return (<ContentEditable html={this.state.html} onChange={handleChange} />);
ContentEditable class
var ContentEditable = React.createClass({
render: function(){
return <div
onInput={this.emitChange}
onBlur={this.emitChange}
contentEditable
dangerouslySetInnerHTML={{__html: this.props.html}}></div>;
},
shouldComponentUpdate: function(nextProps){
return nextProps.html !== this.getDOMNode().innerHTML;
},
emitChange: function(){
var html = this.getDOMNode().innerHTML;
if (this.props.onChange && html !== this.lastHtml) {
this.props.onChange({
target: {
value: html
}
});
}
this.lastHtml = html;
}
});

Categories

Resources