10

Using the example of initializingFromState within Redux-Form, I am trying to set this up dynamically. This is to edit a particular book in a list of books, and is using a simple api set up in express.js.

The full container is below. I somehow need to pass in initialValues, within the mapStateToProps function. In the example, it is done via a static object, but I can't work out how to use the information I have pulled in via fetchBook, and pass it to initialValues.

Container:

import React, { Component, PropTypes } from 'react';
import { reduxForm } from 'redux-form';
import { connect } from 'react-redux';
import { Link } from 'react-router';
import { fetchBook, editBook } from '../actions/index';

class BookEdit extends Component {

  componentWillMount() {
      this.props.fetchBook(this.props.params.id);
  }

    static contextTypes = {
    router: PropTypes.object
    }

  onSubmit(props) {
    this.props.editBook(this.props.book.id, props)
      .then(() => {
        this.context.router.push('/');
      });
  }

    const data = {
        title: {this.props.book.title},
        description: {this.props.author}
    }

  render() {

    const { fields: { title, author }, handleSubmit } = this.props;
    const { book } = this.props;

    if (!book) {
      return (
          <div>
            <p>Loading...</p>
          </div>
      )
    }

    return (
      <div>
      <Link to="/">Back</Link>
        <form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
                <h2>Add a new book</h2>

                <label>Title</label>
                <input type="text" {...title} />
                <div className="text-help">{title.touched ? title.error : ''}</div>

                <label>Author</label>
                <input type="text" {...author} />
                <div className="text-help">{author.touched ? author.error : ''}</div>

                <button type="submit">Add</button>
                <Link to="/" className="button">Go back</Link>
            </form>
      </div>
    );
  }
}

function mapStateToProps(state) {
  return {
      book: state.books.book,
      initialValues: // how do I pass in the books here?
  };
}

export default reduxForm({
  form: 'EditBookForm',
  fields: ['title', 'author']
}, mapStateToProps, { fetchBook, editBook })(BookEdit);

Thank you.

2 Answers 2

13

Your form values aren't what's in state.books.book? I think this is all you're looking for:

function mapStateToProps(state) {
  return {
      book: state.books.book,
      initialValues: state.books.book
  };
}

Since you're only really looking at this.props.book to know if it's loaded or not, it might be more explicit to do something like:

function mapStateToProps(state) {
  return {
      loaded: !!state.books.book,
      initialValues: state.books.book
  };
}

Hope that helps.

Sign up to request clarification or add additional context in comments.

3 Comments

Excellent, thanks so much, you are a star :) Annoyingly the date is coming through in an incorrect format, so am getting the error: The specified value "2016-05-10T13:40:00.000Z" does not conform to the required format, "yyyy-MM-dd". Where would be the best place to convert the format?
That's up to you. Server or client or reducer or component... doesn't matter that much, I don't think.
@ErikR. I am also the similar approach. But I am still wondering that Is it a good idea to keep the same state at two places i.e. first one in state.books.book and the second one will be managed by redux-form?
0

In related to above question, Erik. I have following form and not sure why it is not Validating on submit. It loads the data into fields but when I hit submit the validation fails.

Form_Bayan.js

import React, {Component, PropTypes} from "react";  
import {browserHistory} from "react-router";        
import {reduxForm, Field} from "redux-form";

import {MyCustomInput, MySimpleInput, MyCustomSelect} from "./__form_field_components";

import {connect} from "react-redux";
import {bindActionCreators} from "redux";
import {
    ADMIN_FETCH_AUTOSUGGESTS_Lbl,
    adminFetchAutoSuggestCats_act,
    ADMIN_GENERATESLUG_Lbl,
    adminGenerateSlug_act,
    ADMIN_GETCATID_BYNAME_Lbl,
    adminGetCatIdByName_act,
    ADMIN_ADDNEWBAYAAN_Lbl,
    adminAddNewBayaan_act,
    adminFetchArticlesByCat_act,
    adminUpdateBayaan_act
} from "../../actions/adminActionCreators";
import _ from "lodash";


class NewBayanForm extends Component {

    constructor(props) {
        super(props);       // this component inherits "toggleViewFunction" function through props for redirection

        this.generateSlug = this.generateSlug.bind(this);

        this.state = {
            submitButtonMeta: {
                btnTitle: "Save",
                btnClass: "btn btn-default",
                btnIcon: null,
                disabled: false
            },
            globalMessage: {                        // set when an action is performed by ActionCreation+Reducer and a message is returned
                message: "",
                className: ""
            },
            tempData: {
                //the_bayaansMainCat_id : 1,                // '1' refers to the 'Bayaans' parent category in admin , this ID is used here for different sort of lookups i.e. fetch available subcats for autosuggest, fetch cat ID by name under parent catID
                the_bayaansMainCat_id: this.props.associatedMainCatId,              // being passed from parent component to avoide redundent declaration
                the_autoSuggestCatList: [],
                slug: "",
                the_catId: null
            }
        };
    }

    resetMessageState() {
        var noMsg = {message: "", className: ""};
        this.setState({globalMessage: noMsg});
    }

    componentDidMount() {
        console.log("<NewBayanForm> (componentDidMount)");
        this.props.adminFetchAutoSuggestCats_act(this.state.tempData.the_bayaansMainCat_id);
    }

    doSubmit(props) {
        //console.log("----- submitting form -----");
        //console.log(props);
        this.disableSubmitButton();
        // prepare data for submit request
        // item_title, item_slug, content, picture, attachment, media_path, reference, tag_keywords, author_name, cat_id, date_created
        var newBayanObj = {
            item_title: props.titleTxt,
            item_slug: this.state.tempData.slug,
            content: props.videoIdTxt,
            picture: "",
            attachment: "",
            media_path: "https://www.youtube.com/watch?v=" + props.videoIdTxt,
            reference: "",
            tag_keywords: props.keywordsTxt,
            author_name: props.authorTxt,
            cat_id: this.state.tempData.the_catId
        };


            this.props.adminUpdateBayaan_act(newBayaanObj)
                .then(() => {
                    console.log("%c <NewBayanForm> (doSubmit) Updated bayaan, refetching updated bayaans list...", "color:blue;font-weight:bold;");
                    this.props.adminFetchArticlesByCat_act(this.props.associatedMainCatId)
                        .then(() => {
                            console.log("%c <NewBayanForm> (doSubmit) Redirecting to Gallery after update...", "color:blue;font-weight:bold;");
                            this.props.toggleViewFunction();        // comming from Parent Class (bayaansPage)
                        });
                });


    }

    disableSubmitButton() {
        console.log("<NewBayanForm> (disableSubmitButton)");
        // Ref: http://stackoverflow.com/questions/18933985/this-setstate-isnt-merging-states-as-i-would-expect
        var newButtonState = {
            btnTitle: "Please wait... ",
            btnClass: "btn btn-disabled",
            btnIcon: null,
            disabled: true
        };
        this.setState({submitButtonMeta: newButtonState});
        this.resetMessageState();           // Need to reset message state when retrying for form submit after 1st failure
    }

    enableSubmitButton() {
        console.log("<NewBayanForm> (enableSubmitButton)");
        // Ref: http://stackoverflow.com/questions/18933985/this-setstate-isnt-merging-states-as-i-would-expect
        var newButtonState = {btnTitle: "Save", btnClass: "btn btn-default", btnIcon: null, disabled: false};
        this.setState({submitButtonMeta: newButtonState});
    }


    fetchCategoryId(value) {
        console.log('<NewBayanForm> (fetchCategoryId) input-Value:', value);                // make API call to fetch / generate category ID for this post
        this.props.adminGetCatIdByName_act(value, this.state.tempData.the_bayaansMainCat_id);               // '1': refers to look up under 'Bayaans' parent category for the specified category name
    }

    // will always receive and triggers when there are 'new props' and not old/same props
    componentWillReceiveProps(nextProps) {                      // required when props are passed/changed from parent source. And we want to do some operation as props are changed (Ref: http://stackoverflow.com/questions/32414308/updating-state-on-props-change-in-react-form)
        console.log("<NewBayanForm> (componentWillReceiveProps) nextProps: ", nextProps);       // OK
        //console.log("this.props : ", this.props); // OK
        //console.log("nextProps.siteEssentials.actionsResult : ", nextProps.siteEssentials.actionsResult); // OK
        if (nextProps.hasOwnProperty("siteEssentials")) {       // if action status appeared as Done!
            if (nextProps.siteEssentials.hasOwnProperty("actionsResult")) {     // if action status appeared as Done!



                if (nextProps.siteEssentials.actionsResult[ADMIN_GETCATID_BYNAME_Lbl] !== "FAILED") {
                    var clonedState = this.state.tempData;
                    clonedState.the_catId = nextProps.siteEssentials.actionsResult[ADMIN_GETCATID_BYNAME_Lbl];
                    //var   newTempState = {slug: this.state.tempData.slug, the_catId: nextProps.siteEssentials.actionsResult[ADMIN_GETCATID_BYNAME_Lbl] };
                    this.setState({tempData: clonedState});
                }

                if (nextProps.siteEssentials.actionsResult[ADMIN_FETCH_AUTOSUGGESTS_Lbl] !== "FAILED") {
                    var clonedState = this.state.tempData;
                    clonedState.the_autoSuggestCatList = nextProps.siteEssentials.actionsResult[ADMIN_FETCH_AUTOSUGGESTS_Lbl];
                    this.setState({tempData: clonedState});
                }
                console.log("<NewBayanForm> (componentWillReceiveProps) new-State:", this.state);

            }
        }
    }


    render() {    // rendering Edit form
        const {handleSubmit} = this.props;
        console.log('<NewBayanForm> (render_editForm) this.props:', this.props);
        return (
            <div className="adminForm">
                <form onSubmit={handleSubmit(this.doSubmit.bind(this))}>
                    <div className="col-sm-6">
                        <div className="row">
                            <div className="col-sm-5"><label>Title:</label></div>
                            <div className="col-sm-7"><Field name="titleTxt" component={MySimpleInput}
                                                             defaultValue={this.props.name} type="text"
                                                             placeholder="Enter Title"/></div>
                        </div>
                        <div className="row">
                            <div className="col-sm-5"><label>Slug:</label></div>
                            <div className="col-sm-7">{this.state.tempData.slug || this.props.slug} <input
                                type="hidden" name="slugTxt" value={this.state.tempData.slug}/></div>
                        </div>
                        <div className="row">
                            <div className="col-sm-5"><label>Select Category:</label></div>
                            <div className="col-sm-7"><Field name="catTxt" component={MyCustomSelect}
                                                             defaultValue={this.props.category_name} type="text"
                                                             placeholder="Select or Type a New"
                                                             selectableOptionsList={this.state.tempData.the_autoSuggestCatList}
                                                             onSelectionDone={ this.fetchCategoryId.bind(this) }/>
                                <input type="hidden" name="catIdTxt"
                                       value={this.state.tempData.the_catId || this.props.category_id}/>
                            </div>
                        </div>
                    </div>
                    <div className="col-sm-6">
                        <div className="row">
                            <div className="col-sm-5"><label>Youtube Video ID:</label></div>
                            <div className="col-sm-7"><Field name="videoIdTxt" component={MySimpleInput}
                                                             defaultValue={this.props.content} type="text"
                                                             placeholder="TsQs9aDKwrw"/></div>
                            <div className="col-sm-12 hint"><b>Hint: </b> https://www.youtube.com/watch?v=<span
                                className="highlight">TsQs9aDKwrw</span></div>
                        </div>
                        <div className="row">
                            <div className="col-sm-5"><label>Author/Speaker:</label></div>
                            <div className="col-sm-7"><Field name="authorTxt" component={MySimpleInput}
                                                             defaultValue={this.props.author} type="text"/></div>
                        </div>
                        <div className="row">
                            <div className="col-sm-5"><label>Tags/Keywords:</label></div>
                            <div className="col-sm-7"><Field name="keywordsTxt" component={MySimpleInput}
                                                             defaultValue={this.props.tag_keywords} type="text"/>
                            </div>
                        </div>
                    </div>
                    <div className="row">
                        <div className={this.state.globalMessage.className}>{this.state.globalMessage.message}</div>
                    </div>
                    <div className="buttonControls">
                        <a className="cancelBtn" onClick={this.props.toggleViewFunction}>Cancel</a>
                        <button className={this.state.submitButtonMeta.btnClass}
                                disabled={this.state.submitButtonMeta.disabled}>
                            {this.state.submitButtonMeta.btnTitle}</button>
                    </div>
                </form>
            </div>
        );
    }


}

function validate(values) { // Validate function being called on Blur
    const errors = {};
    if (!values.titleTxt)
        errors.titleTxt = "Enter Title";
    if (!values.catTxt)
        errors.catTxt = "Select/Enter a Category";
    if (!values.videoIdTxt)
        errors.videoIdTxt = "Enter youtube video ID (follow the provided hint)";
    if (!values.keywordsTxt)
        errors.keywordsTxt = "Enter keywords (will help in search)";


    return errors;
}


// ReduxForm decorator 
const newBayanFormAdmin_reduxformObj = reduxForm({
    form: "newBayanFormAdmin",  // any unique name of our form
    validate                // totally equivelent to-->  validate: validate
});


function mapStateToProps({siteEssentials}, ownProps) {
    console.log("<NewBayanForm> (mapStateToProps) siteEssentials:", siteEssentials);
    // 1st param is related to our Redux State, 2nd param relates to our own component props
    var initialValues = {
        titleTxt: ownProps.name,
        slugTxt: ownProps.slug,
        catTxt: ownProps.category_name,
        catIdTxt: ownProps.category_id,
        videoIdTxt: ownProps.content,
        authorTxt: ownProps.author,
        keywordsTxt: ownProps.tag_keywords
    };
    console.log("<NewBayanForm> (mapStateToProps) initialValues: ", initialValues);
    return ({siteEssentials}, initialValues);
};

function mapDispatchToProps(dispatch) {
    return bindActionCreators({
        adminFetchAutoSuggestCats_act,
        adminGenerateSlug_act,
        adminGetCatIdByName_act,
        adminAddNewBayaan_act,
        adminFetchArticlesByCat_act
    }, dispatch);
};


NewBayanForm = connect(mapStateToProps, mapDispatchToProps) (newBayanFormAdmin_reduxformObj(NewBayanForm));

export default NewBayanForm;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.