I have two ponents: one container ponent and one presentational ponent.
The container fetches all the information and actions needed to display a Post using the presentational ponent. On the presentational ponent I have a way where I can toggle between presenting the information and editing it. When I edit and submit the data about the post, I want to read the values from all the different inputs I have so I can dispatch an action.
However, the inputs are not inside a <form id='theForm' onSubmit={onHandleSubmit}>
tag. Instead, all the <input>
and <button type='submit'>
that are outside the <form>
have a form='theForm'
attribute.
I thought that I could have many <input>
outside the form, but as long as the form
attribute pointed towards the corresponding <form>
, I could read it values using the event that handleOnSubmit (e) {...}
recieves. However, I haven't figured out how to do that.
How can I read the values of the inputs on my handleOnSubmit
function? Or this a pletely wrong idea I should abandon?
// PostSummaryContainer.js
import React, { PropTypes, Component } from 'react'
import { connect } from 'react-redux'
import { loadPost, editPost, editPostCancel, editPostSubmit } from '../../actions/posts'
import PostSummaryView from '../../views/details/summary'
class PostSummaryContainer extends Component {
constructor (props) {
super(props)
this.handleOnSubmit = this.handleOnSubmit.bind(this)
this.handleOnCancel = this.handleOnCancel.bind(this)
this.handleOnSubmit = this.handleOnSubmit.bind(this)
}
handleOnEdit (e) {
e.preventDefault()
this.props.editPost()
}
handleOnCancel (e) {
e.preventDefault()
this.props.editPostCancel()
}
handleOnSubmit (e) {
e.preventDefault()
// How do I read the input values? <--------------------
}
ponentWillMount () {
const {
id,
loadPost
} = this.props
loadPost(id)
}
render () {
const {
post,
isLoading,
isEditing
} = this.props
const viewProps = {
bacon,
isLoading,
isEditing,
handleOnEdit: this.handleOnEdit,
handleOnCancel: this.handleOnCancel,
handleOnSubmit: this.handleOnSubmit
}
return (
<PostSummaryView {...viewProps} />
)
}
}
const mapStateToProps = (state, ownProps) => {
const {
params: {
id
}
} = ownProps
const post = state.entities.post[id]
const {
isLoading,
isEditing
} = state.posts
return {
id,
post,
isLoading,
isEditing
}
}
export default connect(
mapStateToProps,
{ loadPost, editPost, editPostCancel, editPostSubmit }
)(PostSummaryContainer)
On my presentation ponent:
// PostSummmaryView.js
import React from 'react'
import moment from 'moment'
function PostSummaryView (props) {
const {
post,
isLoading,
isEditing,
handleOnEdit,
handleOnCancel,
handleOnSubmit
} = props
const formId = 'editPostForm'
return (
isLoading
? <div>Loading...</div>
: <div className='row'>
{isEditing && <form id={formId} onSubmit={handleOnSubmit}><input type='text' name='name' /></form>}
<div className='col-md-6'>
<img src={post.media.url} className='img-responsive' />
{isEditing && <input type='file' name='media' form={formId}/>}
</div>
<div className='col-md-6'>
<h1>{post.name}</h1>
<p>
{moment(post.publicationDate).format('dddd, MMMM Do')}
</p>
<hr />
<p className='text-left'>
{post.description || 'Lorem ipsum dolor sit amet, consectetur adipisici elit...'}
</p>
{isEditing
? <div>
<button className='btn btn-lg btn-default' onClick={handleOnCancel}>Cancel</button>
<button type='submit' className='btn btn-lg btn-default' form={formId}>Submit</button>
</div>
: <button className='btn btn-lg btn-default' onClick={handleOnEdit}>Edit</button>
}
</div>
</div>
)
}
export default PostSummaryView
I have two ponents: one container ponent and one presentational ponent.
The container fetches all the information and actions needed to display a Post using the presentational ponent. On the presentational ponent I have a way where I can toggle between presenting the information and editing it. When I edit and submit the data about the post, I want to read the values from all the different inputs I have so I can dispatch an action.
However, the inputs are not inside a <form id='theForm' onSubmit={onHandleSubmit}>
tag. Instead, all the <input>
and <button type='submit'>
that are outside the <form>
have a form='theForm'
attribute.
I thought that I could have many <input>
outside the form, but as long as the form
attribute pointed towards the corresponding <form>
, I could read it values using the event that handleOnSubmit (e) {...}
recieves. However, I haven't figured out how to do that.
How can I read the values of the inputs on my handleOnSubmit
function? Or this a pletely wrong idea I should abandon?
// PostSummaryContainer.js
import React, { PropTypes, Component } from 'react'
import { connect } from 'react-redux'
import { loadPost, editPost, editPostCancel, editPostSubmit } from '../../actions/posts'
import PostSummaryView from '../../views/details/summary'
class PostSummaryContainer extends Component {
constructor (props) {
super(props)
this.handleOnSubmit = this.handleOnSubmit.bind(this)
this.handleOnCancel = this.handleOnCancel.bind(this)
this.handleOnSubmit = this.handleOnSubmit.bind(this)
}
handleOnEdit (e) {
e.preventDefault()
this.props.editPost()
}
handleOnCancel (e) {
e.preventDefault()
this.props.editPostCancel()
}
handleOnSubmit (e) {
e.preventDefault()
// How do I read the input values? <--------------------
}
ponentWillMount () {
const {
id,
loadPost
} = this.props
loadPost(id)
}
render () {
const {
post,
isLoading,
isEditing
} = this.props
const viewProps = {
bacon,
isLoading,
isEditing,
handleOnEdit: this.handleOnEdit,
handleOnCancel: this.handleOnCancel,
handleOnSubmit: this.handleOnSubmit
}
return (
<PostSummaryView {...viewProps} />
)
}
}
const mapStateToProps = (state, ownProps) => {
const {
params: {
id
}
} = ownProps
const post = state.entities.post[id]
const {
isLoading,
isEditing
} = state.posts
return {
id,
post,
isLoading,
isEditing
}
}
export default connect(
mapStateToProps,
{ loadPost, editPost, editPostCancel, editPostSubmit }
)(PostSummaryContainer)
On my presentation ponent:
// PostSummmaryView.js
import React from 'react'
import moment from 'moment'
function PostSummaryView (props) {
const {
post,
isLoading,
isEditing,
handleOnEdit,
handleOnCancel,
handleOnSubmit
} = props
const formId = 'editPostForm'
return (
isLoading
? <div>Loading...</div>
: <div className='row'>
{isEditing && <form id={formId} onSubmit={handleOnSubmit}><input type='text' name='name' /></form>}
<div className='col-md-6'>
<img src={post.media.url} className='img-responsive' />
{isEditing && <input type='file' name='media' form={formId}/>}
</div>
<div className='col-md-6'>
<h1>{post.name}</h1>
<p>
{moment(post.publicationDate).format('dddd, MMMM Do')}
</p>
<hr />
<p className='text-left'>
{post.description || 'Lorem ipsum dolor sit amet, consectetur adipisici elit...'}
</p>
{isEditing
? <div>
<button className='btn btn-lg btn-default' onClick={handleOnCancel}>Cancel</button>
<button type='submit' className='btn btn-lg btn-default' form={formId}>Submit</button>
</div>
: <button className='btn btn-lg btn-default' onClick={handleOnEdit}>Edit</button>
}
</div>
</div>
)
}
export default PostSummaryView
Share
Improve this question
asked Feb 18, 2016 at 3:30
SparragusSparragus
9131 gold badge10 silver badges24 bronze badges
1 Answer
Reset to default 5Disclaimer: I'm still new to React/Redux, so take this answer with a big grain of salt.
I think your approach is slightly off, in that you shouldn't need to go around and gather any data from inputs when submitting (whether inside or outside the <form>
). Your state should always be in one central, consolidated place.
Given you provide a Cancel option, keeping the Post data updated during an Edit in a separate part of the state is more elegant (IMHO) than directly modifying the "source" Post data.
You could create a reducer for the Edit Post form that keeps key/value pairs for the input fields.
When a user starts editing, you could clone the original Post data into this new part of the state specific to the Edit form. As the user changes the inputs, you could dispatch actions saying "hey, form field X was changed to value Y", which could be reduced onto the state without modifying the original Post data. Pseudo-code example of the state object in this flow:
{
// The actual Post data
post: {
media: {...},
title: "My First Post",
publicationDate: 1455768160589,
description: "Lorem ipsum dolor sit amet"
},
// The temporary Post data in the Edit form
postForm: {
media: {...},
title: "My Turbo-Charged First Post",
publicationDate: 1455769951276,
description: "Updated description yadda blah"
}
}
Then, in your presentation ponent, you could drive the input values off postForm
instead of post
.
Each input would be given a change handler function so updates are immediately reflected in the state (or not reflected, depending on your validation logic/reducers). i.e.:
// In your actions associated with `Post`
// ------------------------------------------
function updateForm(field, value) {
return {
type: UPDATE_FORM,
field,
value
}
}
// In your container
// ------------------------------------------
handleOnEdit(event) {
postActions.updateForm(event.target.name, event.target.value)
}
// In your reducer
// ------------------------------------------
switch (action.type) {
case UPDATE_FORM:
return {
...state,
[action.field]: action.value
}
}
// In your presentational ponent's render() method
// ------------------------------------------
const {postForm, handleOnEdit} = this.props
const descriptionMarkup = (
isEditing
? <input type='text' name='description' value={postForm.description} onChange={handleOnEdit} />
: (post.description || 'Lorem ipsum dolor sit amet, consectetur adipisici elit...')
)
// ...
<p className='text-left'>
{descriptionMarkup}
</p>
If you follow this pattern (and again, not sure it's "right"!), submitting the form bees as simple as doing something with your state's postForm
object. That object should always reflect the latest-and-greatest form input values.
Canceling the form bees as simple as setting the postForm
part of the state tree to {}
. The original Post data remains the same.
Hope this helps jog some ideas...
Some other examples/approaches you could try:
- redux-form by erikras
- stateless-form-examples by nackjicholson
- redux-form-example