问题
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