How to retrieve an input form value with redux-form and formValueSelector - react-redux

I am trying to retrieve a single input value and log it. This is an edit form with existing name value prop.
I am not setting state 'name' with field input's value for some reason. I am not sure how to structure the connect part which I think is my problem. In particular, I am not clear on how to write mapStateToProps to include both my non-form state and form state.
partial scaled down code:
import { Field, reduxForm, formValueSelector } from 'redux-form';
import { connect } from 'react-redux';
import { updateStable } from '../../actions/index';
const selector = formValueSelector('myEditForm');
class EditStuff extends Component {
constructor(props) {
super(props);
this.state = {
name: this.props.name
};
}
componentDidMount() {
this.props.initialize({
name: this.props.name || ''
});
}
componentDidUpdate() {
// this.state.name is not getting set from input value
this.props.updateLocalActiveStuffData(this.state.name);
}
render() {
const { handleSubmit } = this.props;
return (
<div>
<form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
<Field
label="Name"
name="name"
class="name"
type="text"
component={renderField}
/>
<button type="submit" className="btn btn-primary">
Submit
</button>
</form>
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
displayEditForm: state.displayEditForm, //my own non-form related state
name: selector(state, 'name') //form input 'name'
};
}
export default connect(mapStateToProps, { updateStuff })(
reduxForm({
form: 'myEditForm'
})(EditStuff)
);

I think if you can't retrieve the value it's because the name of your form in reduxForm and the formValueSelector name are different 'StableEditForm' != 'myEditForm'
You have more info on selectors here
If you want to initialise your form with values, you should set it from your state in mapStateToProps with the initialValues props, something like this:
function mapStateToProps(state) {
return {
initialValues: state.displayEditForm, // value of your form
};
}
A great exemple here
I hope this can help you

Related

React - Redux Form - handleSubmit

Looking for some insight with the Redux Form handleSubmit function. I am following along to Set Griders course from Udemy. Everything is running smoothly until here. Other error/bugs I was able to research and solve them myself. I am really struggling to find a solution here.
singin.js
import React, { Component } from 'react'
import { reduxForm } from 'redux-form'
import { Checkbox, Form, FormField, Button, Grid, GridColumn, Input } from 'semantic-ui-react'
class SignIn extends Component {
onFormSubmitHandler({ email, password }) {
console.log('Values', email, password)
}
render() {
// this.props are brought to us by reduxForm. Allowing to grab the field inputs and submit action
const { handleSubmit, fields: { email, password } } = this.props
return (
<Grid centered columns={2}>
<GridColumn>
<Form onSubmit={handleSubmit(this.onFormSubmitHandler.bind(this))}>
<FormField>
<label>Email:</label>
<Input placeholder='Email' {...email} />
</FormField>
<FormField>
<label>Password:</label>
<Input placeholder='Password' {...password} />
</FormField>
<FormField>
<Checkbox label='I agree to the Terms and Conditions' />
</FormField>
<Button action='submit'>Sign In</Button>
</Form>
</GridColumn>
</Grid>
)
}
}
export default reduxForm({
form: 'signin',
fields: ['email', 'password']
})(SignIn)
rootReducer
import { combineReducers } from 'redux'
import SetsReducer from './SetsReducer'
import { reducer as form } from 'redux-form'
const rootReducer = combineReducers({
sets: SetsReducer,
form
})
export default rootReducer
The console.log inside onFormSubmitHandler logs: 'Values undefined undefined'
Edit: Project on GitHub
I solved the issue. The fix involved importing 'Field' from redux-form and replacing the input element with elements. Passing {Input} as the component to render

Redux-Form Field-Level Validation: Why aren't the error messages showing?

In using redux-form with React, I'm having an issue where the error messages are not displaying for field-level input validation.
Here is the relevant code from the component:
const renderField = ({input, placeholder, type, meta: {touched, error, warning}}) => (
<div>
<input {...input} placeholder={placeholder} type={type} />
{touched &&
((error && <span>{error}</span>) ||
(warning && <span>{warning}</span>)
)
}
</div>
)
const required = value => {
console.log("required");
return value ? undefined : 'Required';
};
const Question = props => {
const { handleSubmit, onBlur, question, handleClick } = props;
return (
<div className={`question question-${question.name}`}>
<form className={props.className} onSubmit={handleSubmit}>
<div className='question-wrapper'>
<label className={`single-question-label question-label-${question.name}`}>{question.text}</label>
<Field
component={renderField}
type={question.type}
name={question.name}
placeholder={question.placeholder}
onBlur={onBlur}
validate={required}
/>
</div>
</form>
</div>
)
}
export default reduxForm({
form: 'quiz',
destroyOnUnmount: false,
forceUnregisterOnUnmount: true,
})(Question);
When I test it, I see that in the console the UPDATE_SYNC_ERRORS action is being called, and the console.log("required"); is also showing up. But when I navigate to the next question, neither on the screen do I see the error message, nor do I see any evidence of it when I inspect the component with DevTools.
I've been following the example on Field-Level Validation shown in the redux-form docs here: http://redux-form.com/6.7.0/examples/fieldLevelValidation/
Any idea what could be causing this? Thanks in advance!
Well, you have to write a validate function, and pass it to the reduxForm helper or wrapper like this. Redux-form will pass all the form values to this function before the form is submitted.
function validate(values) {
const errors = {};
// Validate the inputs from 'values'
if (!values.name) {
errors.name = "Enter a name!";
}
...
return errors;
}
export default reduxForm({
validate,
form: 'QuestionForm'
})(
connect(null, { someAction })(Question)
);
Hope this helps. Happy Coding !
you can also provide validate like this
const formOptions = {
form: 'yourformname',
validate: validatefunctionname,redux-form
};

the selected option will be restored to unselected status

3sec Demo https://www.youtube.com/watch?v=bo2nNQXbhI8&feature=youtu.be
https://gist.github.com/weichenghsu/407a8862f3382a425fb531b3dedcd6f5
As title, the selected option will be restored to unselected status
And onChange method has no effect for the official tutorial example.
My use case is that when a user picks a value from the dropdown. It should fire an action to fetch other data and render on another form
const chooseTable = ({items, meta:{touched, error}}) => (
<select
onChange={event => {
console.log(this.props.fields);
this.props.tableNameOnChange(event.target.value);
}}>
<option value="">Select</option>
{
items.map((item :any, i: integer) =>
<option key={item.id} value={item.id}>{item.name}</option>
)
}
</select>
)
<Field component={chooseTable}
items={schemaData.tableList}
name="tableName"
>
{/*<option value="#ff0000">Red</option>*/}
{/*<option value="#00ff00">Green</option>*/}
{/*<option value="#0000ff">Blue</option>*/}
</Field>
UIBuilderForm = reduxForm({
form: 'dashbaordUiBuilderForm',
fields: ['tableName']
}
})
(UIBuilderForm as any);
// Decorate with connect to read form values
const selector = formValueSelector('dashbaordUiBuilderForm')
// export default connect(mapStateToProps, mapDispatchToProps)(UIBuilderForm);
export default connect(state => {
const TableSchemaName = selector(state, 'TableSchemaName')
return {
TableSchemaName
}
}
I was banging my head on a similar issue with the react-native picker. Try writing your 'chooseTable' as a component instead of a stateless function and use 'this.state' and 'this.setState' to refer to what value is selected. Here's an example from my picker code:
class ExpirePicker extends React.Component {
constructor(props) {
super(props)
this.state = {
selected: 'ASAP'
}
}
render() {
const { input: { onChange, ...rest }} = this.props
return (
<Picker
style={style.input}
selectedValue={this.state.selected}
onValueChange={(value) => {
this.setState({selected: value})
onChange(value)
}}
{...rest}>
{Object.keys(ExpireTypes).map(renderItem)}
</Picker>
)
}
}
Could you also be using the element's "onChange" event and not binding it to redux-forms "onChange" prop?

how to combine checkbox with text input in reactjs

i am trying to build a Ui component in Reactjs which combines a checkbox and a text input attched to it (instead of a text label) so that if the checkbox is checked , the user can change the text input , and if its unchecked the user will not be able to do so
the final goal is to render outside of the component all of textinputs valus which left checked as a list or as a menu item.
Its should look like this :
Checkbox with Text input
anyone knows how should i do this ? im new to reactjs and got a bit confused how to pass logic between two components(as in here between the checkbox and the text input and between the "combo" component and the outer rendered list) .
thanks in advance !
EDIT1:
well i managed to build the component but i cant make the children call the parent handler (handlerCheckbox , handlerInput)in order to actually make the magic happen.
anything im doing wrong ?
this is the child:
class CheckboxTxtInput extends React.Component{
constructor(props){
super(props);
console.log(props.isChecked)
}
handleCheckboxChild(e) {
this.props.handleCheckbox(e,this.props.id)
}
handleInputChild(e){
this.props.handleInput(e,this.props.id)
}
render(){
return (
<div>
<input type="checkbox" onChange={this.handleCheckboxChild} defaultChecked={this.props.isChecked} />
<input type="text" value={this.props.inputValue} disabled={!this.props.isChecked} onChange={this.handleInputChild}/>
</div>
)
}
}
This is the parent:
export default class Text extends React.Component {
constructor(props) {
super(props);
this.state = {
textItems: [{id:0,inputValue:'text',isChecked:true},{id:1,inputValue:'text',isChecked:true}
,{id:2,inputValue:'text',isChecked:true},{id:3,inputValue:'text',isChecked:true}]
};
this.handleCheckbox = this.handleCheckbox.bind(this);
this.handleInput= this.handleInput.bind(this);
}
handleCheckbox(e,id) {
var stateCopy = Object.assign({}, this.state);
stateCopy.textItems[id].isChecked = e.target.value;
this.setState(stateCopy);
}
handleInput(e,id){
var stateCopy = Object.assign({}, this.state);
stateCopy.textItems[id].text = e.target.value;
this.setState(stateCopy);
}
render () {
return (
<div>
<hr className="divider-long"/>
<UI.sectionDividerLabeled label="Show/Hide Text"/>
<hr className="divider-long"/>
<p>Here you can show\hide your text</p>
<div>
<CheckboxTxtInput id={this.state.textItems[0].id} isChecked={this.state.textItems[0].isChecked}
inputValue={this.state.textItems[0].inputValue} handleInput={this.handleInput}
handleCheckbox={this.handleCheckbox} />
<CheckboxTxtInput id={this.state.textItems[1].id} isChecked={this.state.textItems[1].isChecked}
inputValue={this.state.textItems[1].inputValue} handleInput={this.handleInput}
handleCheckbox={this.handleCheckbox}/>
<CheckboxTxtInput id={this.state.textItems[2].id} isChecked={this.state.textItems[2].isChecked}
inputValue={this.state.textItems[2].inputValue}
handleInput={this.handleInput} handleCheckbox={this.handleCheckbox}/>
<CheckboxTxtInput id={this.state.textItems[3].id} isChecked={this.state.textItems[3].isChecked}
inputValue={this.state.textItems[3].inputValue} handleInput={this.handleInput}
handleCheckbox={this.handleCheckbox}/>
</div>
<RenderText />
</div>
)
}
}
The simplest, React-like way to do this is to have a parent wrapper component - say LabeledCheckbox which contains your Text input and your Checkbox components.
When either of the child components do something, they call a callback provided by the parent, and the parent maintains the state for the two components, passing that state down into the props of both children.
The children in this case would never maintain their own state, instead simply calling callbacks and being prop-fed.
Create one component with checkbox and input field with the state of the checkbox and text field.
And then you can reuse it where you want.
You can do something like this :
class CheckboxTxtInput extends React.Component{
constructor(){
super();
this.state = {
checkbox: false,
inputValue: ""
}
}
handleCheckbox(e){
this.setState({checkbox: e.target.checked})
}
handleInput(e){
this.setState({inputValue: e.target.value})
}
render(){
return (
<div>
<input type="checkbox" onChange={this.handleCheckbox.bind(this)} checked={this.state.checkbox}/>
<input type="text" value={this.state.inputValue} disabled={this.state.checkbox} onChange={this.handleInput.bind(this)}/>
</div>
)
}
}
class Test extends React.Component {
render(){
return (
<div><CheckboxTxtInput /></div>
)
}
}
React.render(<Test />, document.getElementById('container'));
Here is the fiddle.
Hope this helps.

Redux-Form: Not able to change value of input elements

I've been trying to implement a form in MapsAddrForm.jsx using Redux-Form and I can't seem to change the value of my input element. When the page loads, the input element does not respond to keyboard input, and when the form field submits, it returns an empty object to the parent component DistrictFinder. Beyond these two files, I've also added form:formReducer as an argument to combineReducers much like the simple example in the Redux-Form tutorials. Is there any way to restore the ability for the DistrictFinder to receive data objects from the address form? For reference, I'm using React 15.1.0, React-redux 4.4.5, ES6, and Redux-Form 5.3.1, all compiled using Webpack.
MapsAddrForm.jsx
import React, {Component} from 'react';
import { connect } from 'react-redux';
import {reduxForm} from 'redux-form';
class MapsAddrForm extends Component {
constructor(props) {
super(props);
}
render() {
const {fields: {address,address2}, handleSubmit} = this.props;
return (
<form onSubmit={handleSubmit}>
<div>
<input type="text" placeholder="Your address" {...address}/>
</div>
<button type="submit">Submit</button>
</form>
);
}
}
export default reduxForm({
form: 'addressForm',
fields: ['address']
})(MapsAddrForm);
DistrictFinder.jsx
import React, { Component, PropTypes } from 'react'
import MapsAddrForm from './MapsAddrForm.jsx'
import { connect } from 'react-redux'
import { changeAddress } from '../actions/index.jsx'
class DistrictFinder extends Component {
constructor(props) {
super(props);
this.handleAddrSubmit = this.handleAddrSubmit.bind(this);
}
handleAddrSubmit(data) {
console.log("Address received: " + JSON.stringify(data));
}
render() {
const {address, district} = this.props
return (
<div class="GMaps">
<h1>Find your district!</h1>
<MapsAddrForm onSubmit={this.handleAddrSubmit} />
<p>My district number is: {district}</p>
</div>
);
}
}
DistrictFinder.propTypes = {
district: PropTypes.string.isRequired,
dispatch: PropTypes.func.isRequired
};
function mapStateToProps(state) {
const { district } = state.infoChange;
return {
district
};
};
export default connect(mapStateToProps)(DistrictFinder);
I ran into this as well on redux-form#6.2.0
After looking at the source for one of the examples, I noticed the call to combineReducers has an explicit key "form" to the object argument. When I added this explicit key, the fields became editable.
// Correct
const reducer = combineReducers({
form: reduxFormReducer // mounted under "form"
})
If you have an existing app, like I do, you likely have this style es6 syntax from the various redux starters.
// Incorrect: results in the witnessed bad behavior
const reducer = combineReducers({
reduxFormReducer
})
See the combineReducers call at https://github.com/erikras/redux-form/blob/master/examples/simple/src/index.js#L10
It'd be interesting to see if this could be a constant that could be passed in and leveraged by the lib. "form" feels like a easily corruptible "namespace".

Resources