最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Read <input> in a functional (stateless) component - Stack Overflow

programmeradmin3浏览0评论

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
Add a ment  | 

1 Answer 1

Reset to default 5

Disclaimer: 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
发布评论

评论列表(0)

  1. 暂无评论