redux-form/immutable, how to update state in reducer.plugin? - redux

currently I'm trying to use redux-form/immutable in ma brand new project and I'm facing so problems which I can't resolve (aspecially, with this cool reducer.plugin), probably couse I'm kinda new to immutable and stuff like that.
Appreciating and advice or help on topic.
so, first of all:
import { combineReducers } from 'redux-immutable';
import routing from './routeReducer';
import { reducer as formReducer } from 'redux-form/immutable';
const rootReducer = combineReducers({
routing,
form: formReducer.plugin({
signupForm: (state, action) => {
switch(action.type) {
case 'SIGNUP_FACEBOOK':
return {
console.log(state);
}
default:
return state
}
}
})
});
while non of the forms field and touched this console.log(state) returns undefined, so here is the question: how may I initialize the form with some kind of state and actually update in on some sort of action ('SIGNUP_FACEBOOK' for this example)?
here is the form code sample:
import React, { PropTypes } from 'react';
import { Field, reduxForm } from 'redux-form/immutable';
import renderInputControl from '../../../common/InputControl';
import validate from './validate';
const SignUpForm = (props) => {
const { handleSubmit, submitting, invalid } = props;
return (
<form onSubmit={handleSubmit} className='signup__form'>
<Field name='name' type='text' component={renderInputControl} label='Full Name'/>
<Field name='email' type='email' component={renderInputControl} label='Email'/>
<Field name='password' type='password' component={renderInputControl} label='Password'/>
<Field name='tel' type='tel' component={renderInputControl} label='Phone Number'/>
<button type='submit' disabled={submitting || invalid}>Sign Up</button>
</form>
);
};
SignUpForm.propTypes = {
handleSubmit: PropTypes.func.isRequired,
onSubmit: PropTypes.func,
submitting: PropTypes.bool,
invalid: PropTypes.bool
};
export default reduxForm({
form: 'signupForm',
validate
})(SignUpForm);
and the second question is: even after I touch any of the fields (so it actually get some state, e.g. console.log(state) returns Map...), how should I update it, couse code:
state.getIn(['fields', 'name']).set('visited', false).set('touched', false)
which should set 'touched' and 'visited' to false doesn't do anything?
Best of luck!

Related

How to display the updated state in the redux store

I am new to redux. I have a form which i need to print the data entered in the form. I am updating the redux state through the local state,but unable to display the state variables through mapStateToProps method.
Thanks in advance
//Form component in App.js File
import React,{Component} from 'react';
import {action1} from './Actions/action1'
import './App.css';
import {connect} from 'react-redux'
import Display from './Components/Displaylist'
const mapDispatchToProps=(dispatch)=>{
return{
submitHandler:(details)=>dispatch(action1(details))
}
}
class App extends Component {
state={
FirstName:'',
LastName:'',
Age: ''
}
nameHandler=(event)=>{
this.setState({[event.target.name]:event.target.value});
}
SubmitHandler=(event)=>{
event.preventDefault()
/*
const firstname=this.state.details.FirstName
const lastname=this.state.details.LastName
const age=this.state.details.Age
*/
this.props.submitHandler(this.state)
}
render(){
return (
<div className="App">
<form onSubmit={this.SubmitHandler}>
<div className="form-group">
<input
type="text"
placeholder="FirstName"
value={this.state.FirstName}
onChange={this.nameHandler}
className="form-control"
name="FirstName"
/>
</div>
<div className="form-group">
<input
type="text"
placeholder="LastName"
value={this.state.LastName}
onChange={this.nameHandler}
className="form-control"
name="LastName"
/>
</div>
<div className="form-group">
<input
type="text"
placeholder="Age"
value={this.state.Age}
onChange={this.nameHandler}
className="form-control"
name="Age"
/>
</div>
<div className="form-group">
<button type="submit" >Submit Form</button>
</div>
</form>
<Display/>
</div>
);
}
}
export default connect(null,mapDispatchToProps) (App);
//action component
import {SUBMISSION} from '../Constants/actiontypes'
import uuidv4 from 'uuid/v4'
export const action1=(body)=>{
console.log(body)
return{
type:SUBMISSION,
payload:{
id:uuidv4(),
body
}
}
}
//reducer component -->I am using combined reducer even i have only one action
import {SUBMISSION} from '../Constants/actiontypes'
const reducer1=(state=[],action)=>{
if(action.type===SUBMISSION){
return [...state, action.payload];
}
return state
}
export default reducer1
//combine reducer file
import {combineReducers} from 'redux'
import Reducer1 from './reducer1'
const allreducers=combineReducers({details:Reducer1})
export default allreducers
//Display file template
const Display=({details:{firstname,lastname,age}})=>{
return(
<div >
<h1>Firstname:{firstname}</h1>
<h1>LastName:{lastname}</h1>
<h1>Age:{age}</h1>
</div>
)
}
export default Display
//DisplayList file--> I have imported the Display.js (above) file in this.And passing values through this file. Finally i imported this in my App.js file
import React from 'react'
import {connect} from 'react-redux'
import Display from './Display'
const mapStateToProps=(state)=>{
return{
details:state.details
}
}
const List=({details})=>{
console.log(details)
return(
<div>
{details.map(de=>{
console.log(de)
return (
<Display details={de} key ={de.id}/>
)
})}
</div>
)
}
export default connect(mapStateToProps,null)(List)
I am unable to display the value that i have entered .
I am getting FirstName :
LastName:
Age:
but not their corresponding values.
you almost got it! The issue is with what you are passing to the Display component and how you are destructuring the props. Make these changes and it should work.
In the DisplayList Component, change
<Display details={de} key={de.id}/>
to
<Display details={de.body} key={de.id} />
In the Display Component, change
const Display=({details:{firstname,lastname,age}})
to
const Display = ({ details: { FirstName: firstname, LastName: lastname, Age: age }})
Here is a codesandbox example for your referrence:
https://codesandbox.io/s/reactredux-obsht
Let me know if you need any help.
I think the issue is with your initial state in reducer,
const reducer1=(state=[],action)=>{ ... }
In React we have state object, but you initialized an array like state=[].
You need to create an initial state object separately like,
const initialState = {
details : [] //name it as `details`, because you are getting `state.details` in `DisplayList` component
}
Now you can update and return your state as,
const reducer1=(state=initialState, action)=>{
if(action.type===SUBMISSION){
return {...state, details: [...state.details, action.payload] };
}
return state
}

Passing form variables to action creator in React-Redux

In a React-Redux app, I'm trying to pass the form variable from a component to the action creator without using Redux-Form, Formic or any other extension.
myForm.js
import { connect } from "react-redux";
import { fetchData } from "../actions/myActions";
class myForm extends Component {
constructor(props) {
super(props);
this.state = {
from: "",
to: ""
};
this.onFormSubmit = this.onFormSubmit.bind(this);
}
onFormSubmit(event) {
event.preventDefault();
const from = event.target.elements.from.value;
const to = event.target.elements.to.value;
this.setState({
from: from,
to: to
});
this.props.fetchData(
this.state.from,
this.state.to,
);
}
render() {
return (
<div>
<form onSubmit={this.onFormSubmit}>
<div>
<input
type="text"
name="from"
/>
</div>
<div>
<input
type="text"
name="to"
/>
</div>
</form>
</div>
export default connect(
null,
{ fetchData }
)(myForm);
I'm passing the action creator fetchData to myForm component and on form submit invoke the onFormSubmit function, which passes the form variables to fetchData like this:
this.props.fetchData(
this.state.from,
this.state.to,
);
Then inside myActions.js I try to access those form variables and start an API request.
myActions.js
import { FETCH_DATA } from "./types";
import axios from "axios";
const APP_KEY = <my api key>;
export const fetchData = (from,to) => async dispatch => {
const response = await axios.get`${URL}/${from}/to/${to}?app_key=${APP_KEY}`;
dispatch({
type: FETCH_DATA,
payload: response.data.journeys
});
};
Is what I'm trying the right approach?
Unfortunately it seems that variables from and to don't get passed to the action creator inside myAction.js.
setState is an async operation, hence this.props.fetchData is called, before the state is even set. You need to use the callback in the second argument of setStatein myForm.js which is excuted after the state has been updated.
this.setState({
from: from,
to: to
}, () => {
this.props.fetchData(this.state.from,this.state.to)
});
Hope this helps. Happy coding !

InitialValues aren't populating redux form

At the moment, I'm having difficulty populating the input fields for a redux form with initial values. Could someone please tell me what's wrong with the following code, or if this is a known issue? Thanks for the help and support.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm, reset, initialize } from 'redux-form'
const renderField = props => (
<div>
<label>{props.placeholder}</label>
<div>
<input {...props}/>
{props.touched && props.error && <span>{props.error}</span>}
</div>
</div>
)
class EditArtist extends Component {
render() {
const { initialValues} = this.props;
console.log(initialValues)
return (
<form>
<Field name="name" component={renderField} type="text" placeholder="Name"/>
</form>
)
}
}
const validate = values => {
const errors = {};
return errors;
}
const mapStateToProps = (state) => ({
initialValues: {
name: "COOL"
}
});
export default connect(mapStateToProps)(reduxForm({
form: 'edit_artist_form',
validate,
enableReinitialize: true
})(EditArtist));

redux-form not showing errors

In my redux-form I'm having troubles showing the errors back to the form,
here's my form:
import React, { Component } from "react";
import { connect } from "react-redux";
import {
registerUser,
signOutUser
} from "../../actions/redux-token-auth-config";
import { Field, reduxForm, SubmissionError } from "redux-form";
import { FormDiv, SubmitButton, Title, Working } from "./styles";
const renderField = ({ input, label, type, meta: { touched, error } }) =>
<div>
<label>
{label}
</label>
<div>
<input {...input} placeholder={label} type={type} />
{touched &&
error &&
<span>
{error}
</span>}
</div>
</div>;
const errorsFrom = response => response.response.data;
const signUp = (data, dispatch, props) => {
return dispatch(props.registerUser(data))
.then(response => dispatch(signOutUser(data)))
.catch(response => {
throw new SubmissionError(errorsFrom(response));
});
};
const SignUp = props => {
const { error, handleSubmit, pristine, submitting } = props;
return (
<FormDiv>
<Title>subscribe</Title>
<form onSubmit={handleSubmit(signUp)}>
<Field
name="email"
component={renderField}
type="text"
placeholder="Email"
label="Email"
/>
<br />
<Field
name="password"
component={renderField}
type="password"
placeholder="Password"
label="Password"
/>
<br />
<Field
name="passwordConfirmation"
component={renderField}
type="password"
placeholder="Confirm Password"
label="Confirm Password"
/>
<br />
{error &&
<strong>
{error}
</strong>}
<SubmitButton type="submit" disabled={pristine || submitting}>
Sign Up
</SubmitButton>
{submitting && <Working />}
</form>
</FormDiv>
);
};
const ReduxFormSignUp = reduxForm({
form: "signup"
})(SignUp);
const mapStateToProps = (state, props) => {
return {
error:
state.form.signup && state.form.signup.submitErrors
? state.form.signup.submitErrors.errors
: null
};
};
const mapDispatchToProps = dispatch => {
return {
registerUser
};
};
export default connect(mapStateToProps, mapDispatchToProps)(ReduxFormSignUp);
After submitting I get some errors in my redux state:
but for some reason they fail to be passed back in the props. so my props contain handleSubmit, pristine, submitting but no error prop. Regardless whether I use my mapStateToProps or not
Update
There seems to be an incongruence between the documentation and the library, I see no reference to passing errors to fields in here https://github.com/erikras/redux-form/blob/master/src/createField.js, as it's described here: https://redux-form.com/7.2.3/docs/api/field.md/#usage
Similarly here: https://redux-form.com/7.2.3/docs/api/reduxform.md/ I see no references to passing error prop to the wrapped component. But I'm seeing so many examples where I should expect an error prop passed by reduxForm.
Any clues?
Each field error is passed to the Field and the generic error (_) is passed to the form. I think your code should work. Are the fields touched?

Uncaught TypeError when calling an action creator with data from a form input

I am new and would really appreciate your help. I have a form with four inputs (firstName, lastName, position and email) and want to pass the data the user puts in to the state to create an user object. But I always receive this error: Uncaught TypeError: Cannot read property 'firstName' of undefined
Maybe I did map the state wrong? I honestly don't know.
Here is my code:
The form I created which takes the users input:
import React, { Component } from 'react';
import TextField from 'material-ui/TextField';
import { Field, FieldArray, reduxForm} from 'redux-form';
import SelectField from 'material-ui/SelectField';
import MenuItem from 'material-ui/MenuItem';
import validate from './validate';
import injectTapEventPlugin from 'react-tap-event-plugin';
injectTapEventPlugin(); //Needed, otherwise an error message is shown in the console
//Texteingabefeld
const renderTextField = ({input, label, meta: {touched, error}, ...custom}) => (
<TextField
hintText={label}
floatingLabelText={label}
errorText={touched && error}
{...input}
{...custom}
/>
);
const renderUsers = ({fields, meta: { touched, error }}) => (
<div>
<div>
<button className="btn btn-primary"
type="button" onClick={() => fields.push({})}>
<span className="glyphicon glyphicon-plus-sign"/>Add User
</button>
{touched && error && <span>{error}</span>}
</div>
<Field name="firstName" component={renderTextField} label="First Name"/>
<Field name="lastName" component={renderTextField} label="Last Name"/>
<Field name="position" component={renderTextField} label="Position"/>
<Field name="email" component={renderTextField} label="Email"/>
{fields.map((user, index) =>
<div key={index}>
<Field name={`firstName${index}`} component={renderTextField} label="First Name"/>
<Field name={`lastName${index}`} component={renderTextField} label="Last Name"/>
<Field name={`position${index}`} component={renderTextField} label="Position"/>
<Field name={`email${index}`} component={renderTextField} label="Email"/>
<button className="btn btn-xs btn-danger"
type="button"
title="Remove User"
onClick={() => fields.remove(index)}>
<span className="glyphicon glyphicon-minus-sign"/>
</button>
</div>
)}
</div>
);
const UserCreation = props => {
const { handleSubmit, pristine, reset, submitting} = props;
return (
<form onSubmit={handleSubmit}>
<FieldArray name="users" component={renderUsers}/>
<div>
<button className="btn btn-primary btn-success"
type="submit"
disabled={pristine || submitting}>
<span className="glyphicon glyphicon-send" />
Submit
</button>
{' '}
<button type="button"
className="btn btn-primary btn-danger"
disabled={pristine || submitting}
onClick={reset}>
Cancel
</button>
</div>
</form>
);
}
export default reduxForm({
form: 'UserCreationForm',
validate
})(UserCreation);
The Component which fires the action when the user submits the form:
import React, {Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import UserCreation from '../components/UserCreation';
import { addUser } from '../actions/UserActions';
class UserControlPage extends Component {
componentWillMount() {
}
handleSubmit=(values) => {
addUser(values);
}
render() {
return (
<div>
<legend>
<span className="glyphicon glyphicon-user" aria-hidden="true"></span> User creation
</legend>
<UserCreation onSubmit={this.handleSubmit}/>
</div>
);
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({addUser}, dispatch);
}
function mapStateToProps(state) {
return {
users: state.users
}
}
export default connect(mapStateToProps, mapDispatchToProps)(UserControlPage);
The action creator:
import axios from 'axios';
import {ADD_USER} from './index';
export const addUser = (user) => {
return {
type: ADD_USER,
payload: {
// id: id,
firstName: user.payload.firstName,
lastName: user.payload.lastName,
position: user.payload.position,
email: user.payload.email
}
};
}
The reducer:
import {ADD_USER} from '../actions/index';
const INITIAL_STATE = {};
export default function UserReducer(state = INITIAL_STATE, action) {
switch (action.type) {
case ADD_USER:
return [...state, {
// id: action.id,
firstName: action.firstName,
lastName: action.lastName,
position: action.position,
email: action.email
}];
default:
return state;
}
}
The action creator has to get data without the payload, like this:
export const addUser = (user) => {
return {
type: ADD_USER,
payload: {
firstName: user.firstName,
lastName: user.lastName,
position: user.position,
email: user.email
}
}
}
I see a couple things wrong. First of all, you're attaching your data to a payload object inside of your action, but you're not reading from that object in your reducer. So in the reducer, for example, where you have action.firstName, it should actually be action.payload.firstName.
Secondly, what you're returning from the reducer is not right. Your initial state is an object, but you're returning an array. So your state is all kinds of messed up as soon as you mutate state. Try this instead:
import {ADD_USER} from '../actions/index';
const INITIAL_STATE = {};
export default function UserReducer(state = INITIAL_STATE, action) {
switch (action.type) {
case ADD_USER:
return {
...state,
user: action.payload
};
default:
return state;
}
}
Update
I see yet another problem. In this code, you're referencing user.payload, which does not exist.
export const addUser = (user) => {
return {
type: ADD_USER,
payload: {
// id: id,
firstName: user.payload.firstName,
lastName: user.payload.lastName,
position: user.payload.position,
email: user.payload.email
}
};
}
It should be user.firstName, user.lastName, etc.
Thanks guys. Changing the action creator to
export const addUser = (user) => {
return {
type: ADD_USER,
payload: {
firstName: user.firstName,
lastName: user.lastName,
position: user.position,
email: user.email
}
};
}
and the reducer to
const INITIAL_STATE = [];
export default function UserReducer(state = INITIAL_STATE, action) {
switch (action.type) {
case ADD_USER:
return [action.payload, ...state];
default:
return state;
}
}
solved the error.

Resources