React Redux-Form using child components with child components that have multiple submits

雨燕双飞 提交于 2019-12-12 05:19:39

问题


New to React-Redux.

I have a child component that uses the following with "handleSubmit" and this part of the code works.

        <span
        style={textLinkStyle}
        onClick={handleSubmit(values =>
          props.onSubmit({
            ...values,
            sortBy: 'ClientNumber'
          }
          ))}>
        <span>Client # </span>
      </span>

Here is the above code's child component with all the relevant parts.

export const TableHeaders = (props) => {
  const { handleSubmit } = props

  const { sortBy, sortDirection } = props

  return (
    <div>
      <div className="row">
        <div className="col-md-2" style={headingCellStyle}>
          <span
            style={textLinkStyle}
            onClick={handleSubmit(values =>
              props.onSubmit({
                ...values,
                sortBy: 'ClientNumber'
              }
              ))}>
            <span>Client # </span>
          </span>

          <GlyphiconDirectionChange sortDirection={sortDirection}/>
          ......

  )
}
TableHeaders.propTypes = {
  onSubmit: PropTypes.func.isRequired,
}

const TableHeadersForm = reduxForm({
  form: 'TableHeaders',
})(TableHeaders)

export default TableHeadersForm

So I wanted to separate repetative parts of this component into separate smaller components so I could reuse them. So the above component is now the parent of the following.

However it also has multiple submits one for sort direction up and one for sort direction down... I thought it would propagate all the way up to the top parent but alac alas.. no.

This is what I tried.

const GlyphiconDirectionChange = (props) => {
  const { handleSubmit } = props
  const { sortDirection } = props

  return (
    <span>
      {sortDirection === 'Descending' ?
        <span
          style={textLinkStyle}
          onClick={handleSubmit(values =>
            props.onSubmit({
              ...values,
              sortDirection: 'Ascending'
            }
            ))}
          className='glyphicon glyphicon-sort-by-attributes'>
        </span>
        :
        <span
          style={textLinkStyle}
          onClick={handleSubmit(values =>
            props.onSubmit({
              ...values,
              sortDirection: 'Descending'
            }
            ))}
          className='glyphicon glyphicon-sort-by-attributes-alt'>
        </span>
      }
    </span>
  )}

What I found is that handleSubmit works when it is used in the the TableHeader component as part of a larger parent component -its one level down. A first order child.

It does not work in the GlyphiconDirectionChange component. the second order child... Why?

I get the following error in the console

Exception: Call to Node module failed with error: TypeError: handleSubmit is not a function

This is a syntax error. How should I deal with the "handleSubmit" while also sending the updated values when its part of child that is also a child component.

So to recap. HandleSubmit works in the child but doesnt work in the the child component's child.. second level down...

EDIT

I note that #Dennis has suggested I use a despatch instead of using handleSubmit. This is the full ClientsContainer:

import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import Clients from './Clients'
import SearchClients from './SearchClients'
import TableHeaders from './TableHeaders'
import { requestClients } from './reducer'

class ClientsContainer extends Component {
  static contextTypes = {
    router: PropTypes.object.isRequired
  }

  componentDidMount() {
    if (!this.props.isAuthorised) {
      this.context.router.push('/')
      return
    }

    this.fetchClients()
  }

  fetchClients = (values = {}) => {
    const { currentPage, query, sortBy, sortDirection } = this.props
    const searchParams = {
      currentPage,
      query,
      sortBy,
      sortDirection,
      ...values,
    }
    console.log('fetchClients()!', values, searchParams)
    this.props.requestClients(searchParams)
  }

  clientsSearch = (values = {}) => {
    const { query, sortBy, sortDirection } = this.props
    values.query = values.query || ''
    const searchParams = {
      query,
      sortBy,
      sortDirection,
      ...values,
      currentPage: 1,
    }
    console.log('clientsSearch()!', values, searchParams)
    this.fetchClients(searchParams)
  }

  changeHeaders = (values = {}) => {
    const { query, sortBy, sortDirection } = this.props
    values.query = values.query || ''
    const searchParams = {
      query,
      sortBy,
      sortDirection,
      ...values,
      currentPage: 1,
    }
    console.log('changeHeaders()!', values, searchParams)
    this.fetchClients(searchParams)
  }

  handlePageChanged = (pageIndex) => {
    this.fetchClients({
      currentPage: pageIndex,
    })
  }

  render() {
    const { clients, currentPage, query, sortBy, sortDirection, totalClients, totalPages } = this.props

    return (
      <div className="row">
        <div className="col-md-12">
          <SearchClients onSubmit={this.clientsSearch}
            currentPage={currentPage}
            query={query}
            sortBy={sortBy}
            sortDirection={sortDirection}
          />
          <TableHeaders onSubmit={this.changeHeaders}
            currentPage={currentPage}
            query={query}
            sortBy={sortBy}
            sortDirection={sortDirection}
          />
          <Clients clients={clients}
            currentPage={currentPage}
            query={query}
            sortBy={sortBy}
            sortDirection={sortDirection}
            totalClients={totalClients}
            totalPages={totalPages}
            onPageChanged={this.handlePageChanged}
          />
        </div>
      </div>
    )
  }
}

const mapStateToProps = (state) => ({
  isAuthorised: state.login.isAuthorised,
  clients: state.clients.clients,
  currentPage: state.clients.currentPage,
  query: state.clients.query,
  sortBy: state.clients.sortBy,
  sortDirection: state.clients.sortDirection,
  totalClients: state.clients.totalClients,
  totalPages: state.clients.totalPages,
})
const mapDispatchToProps = (dispatch) => ({
  requestClients: (values) => dispatch(requestClients(values)),
})
export default connect(mapStateToProps, mapDispatchToProps)(ClientsContainer)

It does have despatch which is where it goes to the reducer. Again.. if I want to componentize a form into many components one of which might be a header that is a link.. whats the best way to do this when it might be a component of a component of a parent component that at the moment is the only one that despatches...


回答1:


Is it absolutely necessary to submit stuff like sort directions, or is that in some way relevant to your backend? Seems to me like this should be a responsibility for the client.

Leaving it up to the client also has an added benefit of making all this a lot simpler, since you could just dispatch a regular action for when you want to change sort direction. That action could then call your backend with the requested sort direction and that can update the new state accordingly.

Depending on the amount of data you're dealing with, you could even choose to not call your backend at all and leave the sorting entirely up to the client.

Regarding your TableHeaders component, here's what I would do:

Have the component accept an event handler function via props, so you can notify the container when the sorting changes inside the component.

This works just fine for more smaller components, since you're just passing around functions via props.

Simplified example:

export const TableHeaders = (props) => {
  const { onSortByChange, onSortDirectionChange } = props;

  return (
    <div>
      <span onClick={() => onSortByChange('ClientNumber')}>Client #</span>
      <span onClick={() => onSortByChange('SomethingElse')}>Something else</span>

      <GlyphiconDirectionChange onSortDirectionChange={onSortDirectionChange} />
    </div>
  );
}

const GlyphiconDirectionChange = (props) => {
  const { onSortDirectionChange } = props;

  return (
    <div>
      <span onClick={() => onSortDirectionChange('asc')}>Asc</span>
      <span onClick={() => onSortDirectionChange('desc')}>Desc</span>
    </div>
  );
}

Then in your container you can do this:

class ClientsContainer extends Component {
  handleSortDirection = (direction) => {
    // Call Redux action to update your sort direction state
  }

  handleSortBy = (by) => {
    // Call Redux action to update your sort by state
  }

  render() {
    return (
      <div>
        <TableHeaders
          onSortByChange={this.handleSortBy}
          onSortDirectionChange={this.handleSortDirection}
          /* other props */
        />
      </div>
    );
  }
}

In order to get the new data, you could implement componentWillUpdate and check if sort by or sort direction have changed and if so, fetch new data based on updated props.

However, I feel it would be more elegant to move that and the data fetching part from your component into an async action creator. That way your component has less responsibilities.



来源:https://stackoverflow.com/questions/43488036/react-redux-form-using-child-components-with-child-components-that-have-multiple

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!