问题
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.
回答1:
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.
回答2:
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;
来源:https://stackoverflow.com/questions/37116733/dynamically-load-initialvalues-in-redux-form