【问题标题】:Dynamically load initialValues in Redux Form以 Redux 形式动态加载 initialValues
【发布时间】:2016-09-04 03:29:06
【问题描述】:

在 Redux-Form 中使用 initializingFromState 的示例,我正在尝试动态设置它。这是在书籍列表中编辑特定书籍,并使用 express.js 中设置的简单 api。

完整的容器在下面。我不知何故需要在mapStateToProps 函数中传入initialValues。在示例中,它是通过静态对象完成的,但我不知道如何使用通过fetchBook 提取的信息,并将其传递给initialValues

容器:

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);

谢谢。

【问题讨论】:

    标签: javascript reactjs redux redux-form


    【解决方案1】:

    您的表单值不是state.books.book 中的值?我想这就是你要找的所有东西:

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

    由于您只是真正查看this.props.book 以了解它是否已加载,因此执行以下操作可能更明确:

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

    希望对您有所帮助。

    【讨论】:

    • 太好了,非常感谢,你是明星 :) 令人讨厌的是日期格式不正确,所以我收到错误:指定值“2016-05-10T13:40:00.000 Z”不符合要求的格式,“yyyy-MM-dd”。转换格式的最佳位置在哪里?
    • 这取决于你。服务器、客户端、reducer 或组件……没那么重要,我不认为。
    • @ErikR。我也是类似的做法。但我仍然想知道在两个地方保持相同的状态是否是个好主意,即state.books.book 中的第一个,第二个将由redux-form 管理?
    【解决方案2】:

    关于上述问题,Erik。我有以下表格,不知道为什么它在提交时没有验证。它将数据加载到字段中,但是当我点击提交时,验证失败。

    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;
    

    【讨论】:

      猜你喜欢
      • 2016-11-10
      • 2018-06-06
      • 1970-01-01
      • 2018-12-30
      • 2016-12-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-23
      相关资源
      最近更新 更多