I'm new to React and I've been facing a problem since few hours now. Even if I found some topics on Stackoverflow or Google that seems equivalent to my issue, I'm unable to solve it...
I'm using react-select to create a simple form. For now, I have only one multi-select input. I am able to use it as expected but when I press "Submit" I want to retrieve the values selected. I tried with refs, with onChange without success. onChange is never fired, that might be an other issue as well.
var MultiSelect = React.createClass({
onLabelClick: function (data, event) {
console.log(data, event);
},
render: function() {
var ops = []
this.props.categories.forEach(function(category) {
ops.push({ label: category.name, value: category.id });
});
return (
<div>
<Select
name = {this.props.name}
delimiter=" "
multi={true}
allowCreate={true}
placeholder = {this.props.label}
options={ops} />
</div>
);
}
});
var ProductForm = React.createClass({
submit: function () {
console.log("Categories: " + this.state.categories);
},
onCategoryChange: function(e) {
console.log("CATEGORY CHANGED !!!!!!")
this.setState({categories: e.target.value});
},
render: function () {
return (
<form onSubmit={this.submit}>
<MultiSelect label="Choose a Category" name="categories" categories={this.props.categories} onChange={this.onCategoryChange}/>
<button type="submit">Submit</button>
</form>
);
}
});
PS : data categories comes from a Rails controller.
I believe your internal Select component should receive onChange from the props provided to MultiSelect, assuming your intention is to listen to changes on the Select component.
Try something like this inside your MultiSelect's render() method:
return (
<div>
<Select
name = {this.props.name}
delimiter=" "
multi={true}
allowCreate={true}
placeholder = {this.props.label}
options={ops}
onChange={this.props.onChange} />
</div>
);
Side note, I don't think e.target.value is going to work inside onCategoryChange, since react-select doesn't send standard events.
Related
i am developing a form in reactjs using formik plugin plugin link. when i submit form i am getting text fields values but dropdown values are coming empty...
this is my dropdown select
<div className="form-group">
<Field component="select" id="category" name="category" value={this.state.value} className={"form-control"} onChange={ this.handleChange }>
<option value="lokaler">Lokaler</option>
<option value="jobb">Jobb</option>
<option value="saker-ting">Saker & ting</option>
<option value="evenemang">Evenemang</option>
</Field>
</div>
handle submit function
export default withFormik({
enableReinitialize: true,
mapPropsToValues({ category }) {
return {
category: category || ''
}
},
handleSubmit(values, { setStatus, setErrors }){
console.log("data is this: ");
console.log(values); //here i am getting all form fields values except select value returning empty value
console.log("category: "+values.category);// here i always get empty value but not getting selected value
}
i am getting all text fields values in handle submit function but i am not able to get dropdown selected value....kindly tell me how to get dropdown select value in handle submit function ?
I also faced this problem yesterday. My problem was to submit form that contains ant design dropdown. I finally make it work after hours of:
revisiting the Formik Docs
watch Andrew Mead's video Better React Form with Formik again.
also viewing Jared Palmer's Working with 3rd-party inputs #1: react-select
The doc said you need to set onChange, onBlur events to setFieldValue, setFieldTouched props respectively like 3rd-party input example:
<MySelect
value={values.topics}
onChange={setFieldValue}
onBlur={setFieldTouched}
error={errors.topics}
touched={touched.topics}
/>
So to my problem, I need to change a bit:
<Select
{...field}
onChange={(value) => setFieldValue('fruitName', value)}
onBlur={()=> setFieldTouched('fruitName', true)}
value={values.fruitName}
...
>
...
</Select>
Hope this will help.
Here is my CodeSandbox
A more robust way to handle select components whilst using Formik would be to also use Jed Watsons react-select component. The two work together nicely and abstract away a lot of the boilerplate you would normally need to implement to get the component working seamlessly with Formik.
I have forked a simple example from Jared Palmer's react-select / Formik example on codesandbox and adjusted it to reflect your code above.
import "./formik-demo.css";
import React from "react";
import { render } from "react-dom";
import { withFormik } from "formik";
import Select from "react-select";
import "react-select/dist/react-select.css";
const options = [
{ value: "lokaler", label: "Lokaler" },
{ value: "jobb", label: "Jobb" },
{ value: "saker-ting", label: "Saker & ting" },
{ value: "evenemang", label: "Evenemang" }
];
const MyForm = props => {
const {
values,
handleChange,
handleBlur,
handleSubmit,
setFieldValue
} = props;
return (
<form onSubmit={handleSubmit}>
<label htmlFor="myText" style={{ display: "block" }}>
My Text Field
</label>
<input
id="myText"
placeholder="Enter some text"
value={values.myText}
onChange={handleChange}
onBlur={handleBlur}
/>
<MySelect value={values.option} onChange={setFieldValue} />
<button type="submit">Submit</button>
</form>
);
};
class MySelect extends React.Component {
handleChange = value => {
// this is going to call setFieldValue and manually update values.topcis
this.props.onChange("option", value);
};
render() {
return (
<div style={{ margin: "1rem 0" }}>
<label htmlFor="color">Select an Option </label>
<Select
id="color"
options={options}
onChange={this.handleChange}
value={this.props.value}
/>
</div>
);
}
}
const MyEnhancedForm = withFormik({
mapPropsToValues: props => ({
myText: "",
option: {}
}),
handleSubmit: (values, { setSubmitting }) => {
console.log(values);
}
})(MyForm);
const App = () => <MyEnhancedForm />;
render(<App />, document.getElementById("root"));
There are a few gotchas, you have to include the react select css for the component to display properly
import "react-select/dist/react-select.css";
the function mapPropsToValues option field should be initialised to an object
mapPropsToValues: props => ({
myText: "",
option: {}
Finally here is a link to the codesandbox example
Sorry for the general title but I don't know what else to call it at this point. I have a form that uses widgets. Im using the react-jsonschema-form package. Id use the github for the project but I don't think this is bug so I want to check here first. I think it's just how to use this React thing question. Possibly Redux as well.
So...
I have these widgets for some of the form elements in my component.
dropdownWidget = (props, type, id) => {
return (
<div>
<input id={id} type="text" className="form-control" list={type} placeholder="Select one..." onChange={(event) => { props.onChange(event.target.value); this.hideResultTables(event.target.id) }} />
<datalist id={type}>
{this.props.actionsObj.filterJsonData(type, this.props.convertDropDownDataObj).map((value, index) => { return <option key={index} value={value}>{value}</option> })}
</datalist>
</div>
)
}
multiFileWidget = (props) => {
return (
<div>
<OverlayTrigger trigger={['hover', 'focus']} placement="right" overlay={fileWidgetPopup}>
<input type="file" id="multiFileName" required={props.required} onChange={(event) => { props.onChange(event.target.value); this.getFileNames(); this.hideResultTables(event.target.id) }} multiple />
</OverlayTrigger>
<textarea id="multiFileNameList" className="form-control" rows="4" style={{ marginTop: "2%" }} readOnly />
</div>
)
}
dropdownTooltipWidget = (props, type, id, tooltip) => {
return (
<div>
<OverlayTrigger trigger={['hover', 'focus']} placement="right" overlay={tooltip} hidden>
<input id={id} type="text" className="form-control" list={type} placeholder="Select one..." onChange={(event) => { props.onChange(event.target.value); this.hideResultTables(event.target.id) }} />
</OverlayTrigger>
<datalist id={type}>
{this.props.actionsObj.filterJsonData(type, this.props.convertDropDownDataObj).map((value, index) => { return <option key={index} value={value}>{value}</option> })}
</datalist>
</div>
)
}
They are stuffed in a object like so:
multiUISchema = {
file: {
'ui:widget': this.multiFileWidget,
classNames: "uiSchema",
},
convertTo: {
'ui:widget': (props) => this.dropdownWidget(props, "ConvertToTypes", "multiConvertTo"),
classNames: "uiSchema"
},
notification: {
'ui:widget': (props) => this.dropdownTooltipWidget(props, "NotificationType", "notification", websNotificationPopup),
classNames: "uiSchema"
}
}
Where things go bad!! This function is injected in the widgets onChange handler.
hideResultTables(targetId) {
debugger;
//disable Retrieve button
if (targetId === 'multiFileName' || targetId === 'multiConvertTo') {
console.log("BEFORE::", this.state.formData)
selectedFiles = [];
document.getElementById("convertResultTable").setAttribute("hidden", true);
document.getElementById("retrieveResultTable").setAttribute("hidden", true);
this.setState({ disabled: true });
}
//leave Retrieve Button enabled
else if (targetId !== 'appIDs') {
console.log("BEFORE::", this.state.formData)
selectedFiles = [];
document.getElementById("convertResultTable").setAttribute("hidden", true);
document.getElementById("retrieveResultTable").setAttribute("hidden", true);
}
}
Basically if I hit the first if statement which has the setState for disabled to True, my onChange doesn't seem to retain the selected dropdown data to the Form Data object. It's like setState for Disabled true, which is for a button unrelated to the field does something to in the reconciliation tree possibly and/or my form data never retains the selected field in the formdata object at all?? idk. The field just stays undefined in all console logs if it's a field that hits that first if statement with the setState in it. But that setState has nothing to do with the fields being selected. It's disabling a button further down the page is all. Im sure im missing some React lifecycle, reconciliation async something or other. Im thinking of just reduxing this all but this simple bool value seems like it should stay as local state to the component. Any thoughts on things I should be checking before i redux this simple bool for my form?
To Further clarify my question, why does setState in the injected function in onChange seem to disrupt the onChange value update for the field on the form in my Widget when I select a field. That's really what this is all about. We cannot figure out why this is happening.
footnote** The dropdown data in the form is redux store data. So im selecting store data in my onchange in the widgets. Not sure if that's relevant.
I have a textarea
<textarea ref="newText" defaultValue={this.props.children}></textarea>
I need to fetch its value,I tried like this.
this.props.update(this.refs.newText.value,this.props.index);
I also used text instead of value but still it returns undefined.
this.refs.newText.value
Try updating your react-dom libraries. ref help you to imperatively modify a child without the need to use props.get the latest from react docs
src="https://unpkg.com/react#15/dist/react.js">
src="https://unpkg.com/react-dom#15/dist/react-dom.js">
Check out Refs & the DOM
It's hard to debug without the full source code of your component. I've prepared a codepen which shows two ways of accessing the value entered in the <textarea/> element: http://codepen.io/PiotrBerebecki/pen/amJAqb
Have a look also at the React Forms docs: https://facebook.github.io/react/docs/forms.html#controlled-components
const TextArea = React.createClass ({
getInitialState: function() {
return {
userInputValue: '',
userInputRefs: ''
};
},
handleChange: function(event) {
this.setState({
userInputValue: event.target.value
});
this.setState({
userInputRefs: this.refs.userData.value
});
},
render: function() {
return (
<div>
<textarea ref="userData"
type="text"
onChange={this.handleChange}
value={this.state.userInputValue} />
<h3>User input value:</h3>
<p>{this.state.userInputValue}</p>
<h3>User input refs:</h3>
<p>{this.state.userInputRefs}</p>
</div>
);
}
})
ReactDOM.render(
<TextArea />,
document.getElementById('app')
)
I am fairly new to reactJS and I am wondering what's the best way to handle having the same form component on a single page. Please keep in mind that i am using flux and the component is talking to a store.
For example:
< SearchForm />
< SearchForm />
When I try to use form #1 input field, Form #2 gets the value from form #1 at the same time. i think the problem is coming from the store. the components are speaking to the same store and the store is updating all the components at once.
How can i handle this problem?
here is the code i have so far.
const SearchField = React.createClass({
propTypes: {
isSearchActivated: React.PropTypes.bool.isRequired,
},
_onChange() {
var previousHighlightedIndex = this.state.highlightedIndex;
this.setState(getStateFromStores(), function() {
if (previousHighlightedIndex == 0 &&
this.state.highlightedIndex == -1) {
this.refs.SearchBar.selectAll();
}
});
},
componentDidMount() {
if (window.location.pathname == "/" && !Modernizr.mq("screen only and (max-width: 768px)")) {
$(ReactDOM.findDOMNode(this.refs.SearchBar)).find("input").focus();
}
},
componentWillUnmount() {
SearchResultStore.removeChangeListener(this._onChange);
},
onChangeSearchString(e) {
SearchResultsUtils.search(e.target.value);
},
onBlur(e) {
var self = this;
var cb = function() {
if (!self.state.selectedResult && self.state.results.length) {
self.handleSelectedResult(0);
}
SearchResultsActions.disallowResultsDisplay();
};
if($(".search-bar").hasClass("active")) {
$(".search-bar.active").removeClass("active");
}
},
onFocus(e) {
$(ReactDOM.findDOMNode(this.refs.SearchBar)).closest(".search-bar").addClass("active");
},
handleSubmit() {
var self = this;
},
render() {
var className = "search-bar clearfix";
return (
<div className={className}>
<div className="search-bar-search">
<SearchBar
searchString={this.state.searchString}
onChange={this.onChangeSearchString}
onKeyDown={this.onKeyDownSearchString}
onFocus={this.onFocus}
onBlur={this.onBlur}
placeholder="Search Meds or Conditions"
ref="SearchBar" />
</div>
<SearchButton
handleSubmit={this.handleSubmit} />
</div>
);
},
});
module.exports = SearchField;
Thanks for the help in advance.
Definitely you can reuse your components multiple times across your application.
1.If you do not want to have any state:
In your situation you can provide form submit handler as callback prop. If you do not want to maintain any state on your search form.
e.g
For form 1
<Searchform submitHandler={searchForm1Handler}/>
For form 2
<Searchform submitHandler={searchForm2Handler}/>
and Inside your search form component
render(){
return (
<form id="searchform" onSubmit={this.props.submitHandler} role="form">
// other input and buttons
</form>
)
}
2. With states
With this approach each component will have its own separate states which will be private to them.
Here is sample component to illustrate this
import React ,{ Component } from 'react';
export default class SearchForm extends Component {
constructor(props){
super(props);
this.state = {
searchTerm : ''
};
this.submit = this.submit.bind(this);
this.changeSearchTerm = this.changeSearchTerm.bind(this);
}
submit(e){
e.preventDefault();
let searchTerm = this.state.searchTerm;
//Now perform some action based on search term you get
}
changeSearchTerm(e){
this.setState({searchTerm :e.target.value});
}
render(){
return(
<div>
<div className="row">
<div className="col-md-12">
<form role="form" className="form-horizontal" onSubmit={this.submit}>
<fieldset>
<div className="form-group">
<div className="col-sm-6">
<input id="st" type="text" placeholder="search term" onChange={this.changeSearchTerm} value={this.state.searchTerm} required autofocus/>
</div>
</div>
<div className="form-group">
<div className="col-xs-12 text-center">
<button className="btn">
Search
</button>
</div>
</div>
</fieldset>
</form>
</div>
</div>
</div>
)
}
}
Now to use them
For form 1
<Searchform/>
For form 2
<Searchform/>
React has stateful components:
a component can maintain internal state data (accessed via this.state)
which allows you to have <SearchForm />#1 with value1, and <SearchForm />#2 with value2 stored in this.state
To build a form, check this: https://facebook.github.io/react/docs/forms.html
It looks like your store only saves one state - which includes search value - for one form.
If you render this form twice, then both forms retrieve the same state on each change. So they are an exact copy of each other by design. If one form changes, both forms notice the change, and both forms will retrieve the exact same state from the store.
To fix this, I would suggest:
change the store so that it can save a state for multiple forms, each with its own ID
pass this ID to each form as a prop, e.g. <SearchForm formID='form1'/> <SearchForm formID='form2'/>
pass the formID down as a prop to all children of the form, such as <SearchField>
inside the <SearchField>, make sure your component only renders state from its own formID. You probably need to update the getStateFromStores() method itself. Or inside <SearchField> filter out relevant stuff first, before passing it to setState().
when your component informs the store that something has changed (I guess this is what SearchResultsUtils.search(e.target.value); does), then you need to pass the formID as well. So the store will know which form to update.
PS: I think your code is missing some lines in componentDidMount(): you have code to remove the listener to store changes, but your code to add the listener is missing. The code to invoke the _onChange() method is missing from your component.
I have code that is at present accessing a React component directly, and getting a warning saying "You probably don't want to do this." The code is:
var description = document.getElementById('description').value;
console.log(description);
new_child.setState({
description: description
});
The component it's trying to access is:
var that = this;
return (
<table>
<tbody>
{that.state.children}
</tbody>
<tfoot>
<td>
<textarea className="description"
placeholder=" Your next task..."
onChange={that.onChange}
name="description"
id="description"></textarea><br />
<button onClick={that.handleClick}
id="save-todo">Save</button>
</td>
</tfoot>
</table>
);
What is an idiomatic way to replace the code I have here with a "thinking in React" replacement?
You should be using the refs attribute.
So lets say you have a render method that looks like this:
render: function () {
<MyTextBox ref="myText" />
<div>Some other element</div>
}
Now lets say the rendered MyTextBox element has a expode() method. You could call it using:
this.refs.myText.explode()
Essentially, refs has a property myText on it because during render, you provided a ref name to it by writing ref="myText"
You can find more info here
I'd call myself a complete novice here but looking at this code
https://github.com/abdullin/gtd/blob/master/web/components/TaskComposer.jsx
You should be able to bind to the textareas value:
<textarea id="description"
value={that.state.text} ...
and then you can pull out the value in your click handler like this:
var description = this.state.text;
Let me know if this works :)
UPDATE
Just looked at the React home-page (https://facebook.github.io/react/) and the 3rd example An Application seems to follow this pattern as well
var TodoList = React.createClass({
render: function() {
var createItem = function(itemText, index) {
return <li key={index + itemText}>{itemText}</li>;
};
return <ul>{this.props.items.map(createItem)}</ul>;
}
});
var TodoApp = React.createClass({
getInitialState: function() {
return {items: [], text: ''};
},
onChange: function(e) {
this.setState({text: e.target.value});
},
handleSubmit: function(e) {
e.preventDefault();
var nextItems = this.state.items.concat([this.state.text]);
var nextText = '';
this.setState({items: nextItems, text: nextText});
},
render: function() {
return (
<div>
<h3>TODO</h3>
<TodoList items={this.state.items} />
<form onSubmit={this.handleSubmit}>
<input onChange={this.onChange} value={this.state.text} />
<button>{'Add #' + (this.state.items.length + 1)}</button>
</form>
</div>
);
}
});
React.render(<TodoApp />, mountNode);
So to answer you're question I think the React way to do stuff is to bind to this.state.