Redux-Form Initial values - reactjs

So I'm trying to load a Redux Form pre populated with values from my store. However I'm unable to get anything back other than null. Been at this for several hours now and been reading over several SO examples trying different things and think I'm just at a wall on this.
Following this Redux Form Initializing from State example
Redux: 3.6.0
React-Redux: 5.0.3
Redux-Form: 6.6.3
React: 15.4.2
There is a parent component that is rendering a child component which is the form. For sake of brevity going to put in the bare minimum of code and make names as generic as possible. Everything loads fine but I think the issue relies in not properly connecting to my store. Or rather I should just be loading the data on a componentWillMount?
Parent Component:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchUser } from '../actions/usersActions';
import ChildForm from './ChildForm.jsx'
#connect((store) => {
return{
user: store.users.user
}
})
export default class Parent extends Component{
componentWillMount(){
this.props.dispatch(fetchUser(this.props.match.params.id))
}
submit = (values) => {//do some stuff}
render(){
return(
<ChildForm onSubmit={this.submit} />
);
}
}
ChildForm:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import { user } from './reducers/usersReducer.js';
class ChildForm extends Component{
render(){
console.log('FORM STATE >>>>>>>>>>', this.state); //Returns NULL
const { handleSubmit } = this.props;
return(
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="firstName">First Name</label>
<Field name="first_name" component="input" type="text"/>
</div>
<button type="submit">Submit</button>
</form>
);
}
}
ChildForm = reduxForm({
form: 'childForm',
enableReinitialize : true // I found this in another SO Example
})(ChildForm);
ChildForm = connect(
state => ({
user: state.user,
initialValues: state.user
}),
{ fetchUser }
)(ChildForm)
export default ChildForm;
enableReinitialize SO
usersReducer.js
export default function reducer(state={
user: {},
}, action){
switch (action.type){
case "FETCH_USER_FULFILLED":{
return{
...state,
user: action.payload
}
}
}
return state;
}
So this is where I'm at currently. Can get the page, form, and submit all work. However I can't seem to figure out how to get my Store values out and into the form fields. Any help would be greatly appreciated.

Looks like everything is wired up correctly but I wasn't pulling in the correct object in the store.
ChildForm = connect(
state => ({
initialValues: state.users.user
}),
{ fetchUser }
)(ChildForm)
...Always something little

Related

State in redux form doesn't change

The state doesn't change in my react form. console.log(renderselect) doesn't have a value - it shows undefined.
I tried everything but there are no changes.
import React from "react";
import {Field, reduxForm} from "redux-form";
import {connect} from 'react-redux';
import {renderTextField, renderField, validate, warn, renderselect} from "../../../Components/Forms/renders"
class SyncValidationForm extends React.Component {
constructor(props) {
super(props);
this.state = {
errors: {},
opencheck: "hallo"
}
this.handleSelect = this.handleSelect.bind(this);
}
handleSelect = (value) => {
this.setState({"opencheck": value.target.value});
};
render() {
const {handleSubmit, pristine, reset, submitting, opencheck} = this.props;
console.log(opencheck);
return (<form onSubmit={handleSubmit}>
<div className="box-body">
<div className="row">
<div className="col-lg-3">
<Field className="form" name="favoriteColor" label="Maak eerste een keuze" component={renderselect} options={{
"Geen informatie" : 'Geen informatie',
'Altijd open' : 'Altijd open'
}}
onChange={this.handleSelect}/>
</div>
</div>
</div>
</form>);
}
};
SyncValidationForm = reduxForm({
form: 'insertstart', enableReinitialize: true,
warn
})(SyncValidationForm);
const mapStateToProps = state => {
state => ({
initialValues: { favoriteColor: 'Geen informatie' }
})
}
export default connect(mapStateToProps)(SyncValidationForm)
The selected box must change the state, but there are no changes. Also, in the first render, the opencheck in console log has no value.
You are conflating Redux state (which is mapped to your React component's props in mapStateToProps) with the built-in React component state which has nothing to do with Redux. Before continuing you may wish to read these links to fully understand the difference:
Redux: Organizing State
React: State and Lifecycle
It's often confusing and frustrating that both of these commonly-integrated libraries both call their data source "state", but it will be important for your to understand the difference and identify which one you are using.
If you want to use Redux to store the selected state, your handleSelect function needs to dispatch the new value to the Redux store, typically by using mapDispatchToProps to dispatch an action, not by calling this.setState
If you want to use React component state, then you need to refer to this.state.opencheck in your render() function, not this.props. opencheck.

React-Redux-Form save data to prop from onChange

I am new react-redux and I am trying to save to props the value entered in from a react-redux-form textbox by using the onChange event which can be passed to another component
My code snippet for the textbox is
<ListItemContent>
<Control component={Textfield} model="somemodel" label="MyLabel"
onChange={this.props}/>
</ListItemContent>
How can I save this value and make this available to other components?
EDIT I have it partly working:
<ListItemContent>
<Control component={Textfield} model="somemodel" label="MyLabel"
onBlur={this.onChangeOfValue}/>
</ListItemContent>
onChangeOfValue = (event) =>
{
this.setState({ newValueToPassAlong: event.target.value}); //newValueToPassAlong is set in constructor
};
.....
let mapStateToProps = (state) => {
return {newValueToGive: state.newValueToPassAlong} //This is undefined
};
export default connect(mapStateToProps)(form)
Further, my componentWillReceiveProps(nextProps) is not being fired when the other component's state changes.
// YOUR TEXTFIELD COMPONENT
import React, { Component } form 'react';
import { reduxForm, Field } from 'redux-form';
import {passValueToOtherComponent} from '../actions/your-actions-index-file';
import { connect } from 'react-redux';
import ListItemContent form 'list-item-content';
class TextField extends Component {
constructor(props) {
super(props);
this.state = {
textFieldValue: '',
}
this.onInputChange = this.onInputChange.bind(this);
}
onInputChange(event) {
var newValue = event.target.value;
this.setState({textFieldValue: newValue});
//when input changes
//call action to update global state...
this.props.passValueToOtherComponent(this.state.textFieldValue)
}
render() {
<form>
<ListItemContent>
<Control component={Textfield} model="somemodel" label="MyLabel"
onChange={this.onInputChange} value={this.state.textFieldValue}/>
</ListItemContent>
</form>
}
}
//ReduxForm wrapper
const wrappedReduxForm = connect(null, {passValueToOtherComponent})(TextField)
export default reduxForm({
form: 'TextField'
})(TextField)
// actions/your-actions-index-file.js
//create an action which will call to update global state
export const NEW_VALUE = "NEW_VALUE"
export function passValueToOtherComponent(value) {
return {
type: CREATE_POST,
payload: value,
}
}
//YOUR NewValue Reducer
//reducer_new_value.js
//create reducer which will accept payload data
import {NEW_VALUE} from '../actions/your-actions-index-file';
const INITIAL_STATE = {
valueToPass: null
};
export default function (state = [], action) {
console.log('Action...' action);
switch (action.type) {
case NEW_VALUE:
return { ...state, valueToPass: action.payload.data}
break;
default:
}
}
//Your Root Reducer
//Because you may have lots of state to manage, a rootReducer is awesome in managing it all
import { combineReducers } from 'redux';
import NewValueReducer from './reducer_new_value';
const rootReducer = combineReducers({
// state: (state = {}) => state
value: NewValueReducer,
});
export default rootReducer;
//finally pass this desired value to your desired Component
import React, {Component} from 'react';
import { connect } from 'react-redux';
class OtherComponent extends Component {
render() {
return (
<div>
<input value= {this.props.texFieldValue}>
</div>
)
}
}
function mapStateToProps(state) {
return { textFieldValue: state.value.valueToPass }
}
export default connect(mapStateToProps)(OtherComponent);
This is something I just typed out. Not sure if it will work, but it covers actions, reducers, and updating values from one component to anohter. Of course, this is a crazy way to do it using react redux. I'm not sure how efficient it would be to call this action on every input change. You might be better off passing the current value of state as a prop to your desired component.
If you have some more questions, I'd be happy to help or point you to some other resources.

shall we use state or props for handling inputs in redux react?

I am confused with props or state to use here. If I use state in #connect I get error and does not work. when I use props it does not work with onchange handler to set new props. Please help how should I make input working with state or props. I am retrieving initial data from api.
import React, {PropTypes} from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import { asyncConnect } from 'redux-async-connect';
import {load, isLoaded} from 'redux/modules/overview';
#asyncConnect([{
promise: ({ store: { dispatch, getState }, params: { id }, }) => {
const promises = [];
if (!isLoaded(getState())) {
promises.push(dispatch(load(id)));
}
return Promise.all(promises);
}
}])
#connect(
state => ({
overview: state.overview.data
}),
dispatch => bindActionCreators({load}, dispatch))
export default class Overview extends React.Component {
changeinput1(e) {
this.props.overview.title = e.target.value;
// changing value does not work here
}
constructor (props) {
super();
this.state = {
overview: null,
}
}
render() {
return (
<div>
<label>Input 1</label>
<input type="text" className="form-control" id="title" name="title" maxlength="35" value={this.props.overview.title} onChange={this.changeinput1.bind(this)}/>
</div>
)
}
}
I also want to do validation and want to save input value on onBlur so I dont want to use form.
if you want change reducer's(here suppose to be 'overview') value, you should define an action then dispatch it, not change it directly, the state get from store is readonly in the component

How to export mapStateToProps and Redux Form?

I'm using Redux Form (ver. 6) for a log in page. What I would like to do is when the user fills out the form and clicks submit, grab the text from my state so that I can eventually dispatch an action with that email and password. However, I'm having trouble with exporting this component while using both connect from react-redux and Redux Form.
Using react-redux, connect wants to be exported like so when mapping state to props:
export default connect(mapStateToProps)(LogInForm)
However, Redux Form wants it's export set up like this:
export default reduxForm({
form: 'LogInForm',
validate,
})(LogInForm);
Is there a way to combine these two? I'd tried something like:
const reduxFormConfig = reduxForm({
form: 'LogInForm',
validate,
});
export default connect(mapStateToProps)(ReduxFormConfig)(LogInForm)
but it did not work.
Or Perhaps that's a better approach to handling this? Here is the full code from within my component:
import React from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import InputField from '../InputField';
import { validateLogInSignUp as validate } from '../../utils/validateForms.js';
const LogInForm = (props) => {
const {
handleSubmit,
pristine,
submitting,
} = props;
return (
<div>
<form onSubmit={handleSubmit}>
<Field
name="email"
type="email"
component={InputField}
label="email"
/>
<Field
name="password"
type="password"
component={InputField}
label="password"
/>
<div>
<button type="submit" disabled={submitting}>Submit</button>
</div>
</form>
</div>
);
};
const mapStateToProps = state => {
return {
loginInput: state.form,
};
};
// export default connect(mapStateToProps)(LogInForm)
// export default reduxForm({
// form: 'LogInForm',
// validate,
// })(LogInForm);
Any and all help is much appreciated. Thanks!
There still a way to combine these two in reduxForm v6:
reduxForm({
form: 'LogInForm',
validate,
})(LogInForm);
export default connect(mapStateToProps)(LogInForm)
Here is how:
import React, { Component } from 'react';
import { connect } from 'react-redux
class LogInForm extends Component {
...
}
function mapStateToProps(state) {
return { ... }
}
function mapDispatchToProps(dispatch) {
return { ... }
}
// First decorate your component with reduxForm
LogInForm = reduxForm({
form: 'LogInForm',
validate,
})(LogInForm);
// Then connect the whole with the redux store
export default connect(mapStateToProps, maDispatchToProps)(LogInForm)
Using redux-form you shouldn't need to access the state directly in your LoginForm. Instead, you should access the values in your parent component when the form is submitted:
// LoginForm.js
const LogInForm = (props) => {
...
};
export default reduxForm({
form: 'LogInForm',
validate,
})(LogInForm);
// Parent.js
import LoginForm from './LoginForm';
const handleSubmit = (values) => {
alert(JSON.stringify(values)); // { email: 'foo#bar.com', password: '1forest1' }
};
const Parent = (props) => {
return (
<LoginForm onSubmit={ handleSubmit } />
);
};
See https://github.com/erikras/redux-form/blob/master/examples/simple/src/index.js#L42 for a more complete example of a simple form.
You can try this :
import React from 'react';
import { connect } from 'react-redux' ;
import { reduxForm } from 'redux-form';
class Example extends React.Component {
...
}
function mapStateToProps(state) {
return { ... }
}
function mapDispatchToProps(dispatch) {
return { ... }
}
export default connect(mapStateToProps, mapDispatchToProps)(reduxForm({
form: 'formname',
validate
})(Example));
There are multiple threads with same issue. I recently posted my take with my solution here but with a few variations.
I used a class based component than a function based. I have found guides to specifically implement that way and explaining that the connect method of react-redux make it a Higher Order Component and as such it is best used with a class instance.
As mentioned by #doncredas, reduxForm and react-redux's connect method should be implemented separately, however, my implementation is as follows:
function mapStateToProps(state) {
return { form: state.form };
}
Signin = connect(mapStateToProps, actions)(Signin);
Signin = reduxForm({
form: 'signin'
})(Signin);
export default Signin;
[Added later] Another variation can be:
const form = reduxForm({ form: 'signin' });
export default connect(mapStateToProps, actions)(form(Signin));

Handling updates to store with React/Redux and lifecycle events

I'm using React with Redux on my front end and using the Rails API to handle my backend. At present, I am trying to update a list of articles based on user addition of an article. The ArticleForm component fires an action creator that is successfully updating my ArticleList. However, at present the life cycle method componentWillUpdate is firing continuously making axios requests to Rails, and Rails keeps querying my database and sending back the articleList.
Note: I have tried using shouldComponentUpdate as such to no avail, the DOM doesn't update:
// shouldComponentUpdate(newProps){
// return newProps.articleList !== this.props.articleList
// }
My question is: how can I use React's lifecycle methods to avoid this from happening and only happening when my articleList updates. Am I going down the wrong path using lifecycle methods? I'm fairly new to React/Redux so any and all advice is helpful!
I have the following container:
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import ArticleForm from './ArticleForm'
import ArticleList from './ArticleList'
import removeArticle from '../actions/removeArticle'
import fetchArticles from '../actions/fetchArticles'
import updateArticleList from '../actions/updateArticleList'
class DumbArticleContainer extends Component {
componentWillMount() {
this.props.fetchArticles()
}
// shouldComponentUpdate(newProps){
// return newProps.articleList !== this.props.articleList
// }
componentWillUpdate(newProps){
if (newProps.articleList.articleList.count !== this.props.articleList.articleList.count){
this.props.updateArticleList()
}
}
render() {
return(
<div>
<ArticleForm />
<ArticleList articleList={this.props.articleList} />
</div>
)
}
}
const ArticleContainer = connect(mapStateToProps, mapDispatchToProps)(DumbArticleContainer)
function mapStateToProps(state) {
return {articleList: state.articleList}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({removeArticle, fetchArticles, updateArticleList}, dispatch);
}
export default ArticleContainer
here is the ArticleForm
import React, { Component, PropTypes } from 'react'
import { reduxForm } from 'redux-form'
import addArticle from '../actions/addArticle.js'
class ArticleForm extends Component {
constructor(props) {
super(props)
this.state = {disabled: true}
}
/* Most article elements are displayed conditionally based on local state */
toggleState(){
this.setState({
disabled: !this.state.disabled
})
}
handleFormSubmit(props) {
event.preventDefault()
const {resetForm} = this.props
this.props.addArticle(props).then( ()=>{
var router = require('react-router')
router.browserHistory.push('/dashboard')
resetForm()
})
}
render() {
const disabled = this.state.disabled ? 'disabled' : ''
const hidden = this.state.disabled ? 'hidden' : ''
const {fields: {title, url}, handleSubmit} = this.props;
return (
<div className="article-form">
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
<button className="article-form-btn"
hidden={!hidden}
onClick={this.toggleState.bind(this)}
>
+ Add Article
</ button>
<input className="article-form-input"
hidden={hidden}
type="textarea"
placeholder="Title"
{...title}
/>
<input className="article-form-input"
hidden={hidden}
type="textarea"
placeholder="Paste Link"
{...url}
/>
{ this.state.disabled
? ''
: <input className="article-form-input"
type="submit"
value="Save"
/>
}
</form>
</div>
);
}
}
export default reduxForm({
form: 'articleForm',
fields: ['title', 'url']
},
null,
{ addArticle })(ArticleForm);
and the ArticleList
import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import removeArticle from '../actions/removeArticle.js'
import fetchArticles from '../actions/fetchArticles'
import { ListGroup } from 'react-bootstrap'
import { ListGroupItem } from 'react-bootstrap'
class Article extends Component {
render(){
var articleList = this.props.articleList.articleList
return(
<div>
<ListGroup>
{ articleList.slice(articleList.length - 10, articleList.length)
.map( (article) => {
return(
<ListGroupItem href="#" header={article.attributes.title}>
{article.attributes.url}
</ListGroupItem>
)}
)}
</ListGroup>
<div> View All Articles </div>
</div>
)
}
}
const ArticleList = connect(mapStateToProps, mapDispatchToProps)(Article)
function mapStateToProps(state) {
return {articleList: state.articleList}
}
function mapDispatchToProps(dispatch) {
return {removeArticle: bindActionCreators({removeArticle}, dispatch),
fetchArticles: bindActionCreators({fetchArticles}, dispatch)
}
}
export default ArticleList
action creator:
So here is my action creator import axios from 'axios'
import axios from 'axios'
function updateArticleList(){
const url = 'http://localhost:3000/api/v1/articles'
return axios.get(url).then( (response)=> {
return {
type: 'UPDATE_ARTICLE_LIST',
payload: response.data
}
})
}
export default updateArticleList
and reducer:
export default function articleReducer(state = {articleList: []}, action) {
switch(action.type){
case 'FETCH_ARTICLES':
return Object.assign({}, state, {articleList: action.payload.data});
case 'UPDATE_ARTICLE_LIST':
return Object.assign({}, state, {articleList: action.payload.data});
default:
return state
}
}
There is no issue with the store nor the action creators nor the reducers, they are all working pretty well. I can't really replicate the hundreds of queries rails is performing but am happy to include other code should anyone need to see it.
Thanks!
Your mapDispatchToProps is using bindActionCreators wrong. Instead of
function mapDispatchToProps(dispatch) {
return {removeArticle: bindActionCreators({removeArticle}, dispatch),
fetchArticles: bindActionCreators({fetchArticles}, dispatch)
}
}
you should use
function mapDispatchToProps(dispatch) {
return bindActionCreators({removeArticle, fetchArticles}, dispatch);
}
bindActionCreators can, as the name suggests, bind more than one action creator.
This probably won't solve your issue but an answer is the only place I could put this nicely.
Note that you'll need to fix how you're using it as well. No more double names.
I'd like to keep a state called shouldUpdateList. Whenever I fire a action that changes the list(add or update an item to the list), I set shouldUpdateList to true. Then,set it back to false whenever I fire ajax action to fetch the list.
The lifecycle event I use to check shouldUpdateList is componentWillReceiveProps, if it's true I fire a fetch action.
EDIT: I mean keep shouldUpdateList state in Redux store. Something like:
const INIT_STATE = {
list: [],
shouldUpdateList: false
}
then
case Action.ADD_NEW:
//set shouldUpdateList to true
case Action.FETCH_LIST:
//set shouldUpdateList to false
lastly, in component
componentWillReceiveProps(nextProps) {
if(nextProps.shouldUpdateList === true) {
//dispatch action FETCH_LIST
}
}

Resources