新手。
我也在使用Redux。按照良好做法,我有一个容器“AddressContainer”及其组件“Address”。
AddressContainer如下 -
body {
height:100vh;
width:100vw;
background:linear-gradient(160deg, red, red 60%, white 60%, white);
}
地址组件如下 -
import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import { Field, change } from 'redux-form'
import { Col, Panel, Row } from 'react-bootstrap'
import Select from 'react-select'
import Address from './address'
import { ensureStateData, getSuburbs } from './actions'
import { CLIENT_FORM_NAME } from '../clients/client/client'
export class AddressContainer extends Component {
static contextTypes = {
_reduxForm: PropTypes.object.isRequired,
}
constructor(props, context) {
super(props, context)
this.state = {
selectedSuburb: null,
}
}
componentDidMount() {
this.props.ensureStateData()
}
// Manage asyncSelect for new data request - for suburbs.
handleSuburbSearch = (query) => {
const { addressData } = this.props
const companyStateId = addressData.companyStateId
if (!query || query.trim().length < 2) {
return Promise.resolve({ options: [] })
}
const queryString = {
query: query,
companyStateId: companyStateId,
}
return getSuburbs(queryString)
.then(data => {
return { options: data }
})
}
render() {
const {
initialValues,
addressData,
updatePostcodeValue,
} = this.props
//const { value } = this.state
const sectionPrefix = this.context._reduxForm.sectionPrefix
if (addressData.isLoading || !addressData.states.length) {
return (
<p>Loading</p>
)
}
if (addressData.error) {
return (
<p>Error loading data</p>
)
}
const companyStateId = addressData.companyStateId
// initialValues = {
// ...initialValues.Address=null,
// state: addressData.states.find(option => option.stateId === companyStateId),
// }
return (
<Address
initialValues={initialValues}
addressData={addressData}
handleSuburbSearch={this.handleSuburbSearch}
/>
)
}
}
const mapStateToProps = (state) => ({
initialValues: state.address,
companyStateId: state.companyStateId,
addressData: state.addressData,
})
const mapDispatchToProps = (dispatch) => ({
ensureStateData: () => dispatch(ensureStateData()),
getSuburbs: (values) => dispatch(getSuburbs(values)),
updatePostcodeValue: (postcode, sectionPrefix) => dispatch(change(CLIENT_FORM_NAME, `${sectionPrefix ? (sectionPrefix + '.') : ''}postcode`, postcode))
})
export default connect(mapStateToProps, mapDispatchToProps)(AddressContainer)
问题在于下面的地址组件中的以下函数以及它所说的未定义的setState -
import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import { Field, reduxForm, change } from 'redux-form'
import { Col, Panel, Row } from 'react-bootstrap'
import Select from 'react-select'
import FormField from '../formComponents/formField'
import TextField from '../formComponents/textField'
import StaticText from '../formComponents/staticText'
export const ADDRESS_FORM_NAME = "Address"
export const Address = (props) => {
const { addressData, handleSuburbSearch } = props
const { reset } = props
return (
<Panel header={<h3>Client - Address Details</h3>}>
<Row>
<Field component={TextField}
name="address1"
id="address1"
type="text"
label="Address Line 1"
placeholder="Enter street 1st line..."
fieldCols={6}
labelCols={3}
controlCols={9}
/>
<Field component={TextField}
name="address2"
id="address2"
type="text"
label="Address Line 2"
placeholder="Enter street 2nd line..."
fieldCols={6}
labelCols={3}
controlCols={9}
/>
</Row>
<Row>
<Field
component={props => {
const { input, id, placeholder, type } = props
const { fieldCols, labelCols, controlCols, label, inputClass } = props
// just the props we want the inner Select textbox to have
const { name, onChange } = input
const onStateChange = (state) => {
console.log('onStateChange', state)
onChange(state)
}
return (
<FormField
id={id}
label={label}
fieldCols={fieldCols}
labelCols={labelCols}
controlCols={controlCols}
inputClass={inputClass}
>
<Select
name={name}
onChange={onStateChange}
placeholder="Select state"
valueKey="id"
options={addressData.states}
labelKey="stateLabel"
optionRenderer={option => `${option.stateShortName} (${option.stateName})`}
value={input.value}
selectValue={Array.isArray(input.value) ? input.value : undefined}
/>
</FormField>
)
}}
name="state"
id="state"
label="State."
fieldCols={6}
labelCols={3}
controlCols={6}
/>
</Row>
<Row>
<Field
component={props => {
const { input, id, placeholder, type } = props
const { fieldCols, labelCols, controlCols, label, inputClass } = props
const { name, value, onChange, onBlur, onFocus } = input
const inputProps = {
name,
value,
onChange,
onBlur,
onFocus,
}
const onSuburbChange = (value) => {
console.log('onSuburbChange: ', value)
this.setState({ selectedSuburb: value }, () => {
input.onChange(value)
updatePostcodeValue(value ? value.postcode : null, sectionPrefix)
})
}
return (
<FormField
id={id}
label={label}
fieldCols={fieldCols}
labelCols={labelCols}
controlCols={controlCols}
inputClass={inputClass}
>
<Select.Async
{...inputProps}
onChange={onSuburbChange}
valueKey="id"
labelKey="suburbName"
loadOptions={handleSuburbSearch}
backspaceRemoves={true}
/>
</FormField>
)
}}
name="suburb"
id="AddressLocation"
label="Suburb."
fieldCols={6}
labelCols={3}
controlCols={9}
/>
</Row>
<Row>
<Field component={StaticText}
name="postcode"
id="postcode"
label="Postcode."
fieldCols={6}
labelCols={3}
controlCols={9}
/>
</Row>
</Panel>
)
}
Address.propTypes = {
handleSuburbSearch: PropTypes.func.isRequired,
}
const AddressForm = reduxForm({
form: ADDRESS_FORM_NAME,
})(Address)
export default AddressForm
您会注意到有一个“value”的console.log。这会产生结果:
const onSuburbChange = (value) => {
console.log('onSuburbChange: ', value)
this.setState({ selectedSuburb: value }, () => {
input.onChange(value)
updatePostcodeValue(value ? value.postcode : null, sectionPrefix)
})
}
我使用React-Select作为异步下拉列表。这一切都有效。如果我选择一个选项,我会得到下拉选项,但选择一个,它会给我错误。
我在这里使用的反应状态是selectSuburb选项,因为我不需要用这个来更新redux - 只是反应状态。
似乎没事,但我仍然得到错误。为什么我会收到此错误以及如何解决?
答案 0 :(得分:1)
此特定错误是由<Address />
组件是无状态功能组件且其中不能包含this.state
对象或setState
函数引起的。但是,更一般地说,您希望<AddressContainer />
组件中的状态和函数可用于子<Address />
组件,但这不可能发生。在这种情况下,您希望通过调用孩子的setState
来修改父级的状态。
子React组件(在这种情况下为<Address />
)只有其父级的状态/函数/属性,显式地作为道具传递给该组件。如果你想要更改必须在具有本地状态的组件上发生的组件的本地状态。如果你想让一个子组件触发父类的某种类型的函数调用,那么该函数必须作为prop传递给孩子,孩子可以调用它。
如果我正确理解您的代码,那么当Suburbs FormField按此顺序更改时,您希望发生3件事。
selectedSuburb
上的<AddressContainer />
状态已更新。onChange
中的Redux-Form <Field />
的{{1}}被触发。<Address />
行动被解雇。如果这是正确的,那么您需要将updatePostCode
移至onSuburbChange
并将其作为道具传递给<AddressContainer />
。但是,您无法在<Address />
内拨打Redux-Form onChange
的{{1}}。因此,您可以使该函数期望接收将在状态更新后触发的回调函数。然后,您可以在子组件中定义回调,但在父组件中定义状态更改函数。只要您传递父母所需的道具,例如<Field />
和<AddressContainer />
,您就会变得金色。这就是它的样子:
<强> AddressContainer 强>
updatePostCode
<强>地址强>
sectionPrefix
正如您所看到的,export class AddressContainer extends Component {
/* Everything else in this component */
onSuburbChange = (value, callback) => {
this.setState({ selectedSuburb: value }, callback);
}
render() {
/* Other stuff inside render */
return (
<Address
initialValues={initialValues}
addressData={addressData}
handleSuburbSearch={this.handleSuburbSearch}
onSuburbChange={this.onSuburbChange}
updatePostcodeValue={this.props.updatePostcodeValue}
sectionPrefix={sectionPrefix}
/>
);
}
}
组件中的不同export const Address = (addressProps) => {
return (
/* All other JSX */
<Field
component={props => {
const { input } = props;
const handleSuburbChange = (value) => {
addressProps.onSuburbChange(value, () => {
input.onChange(value);
addressProps.updatePostcodeValue(value ? value.postcode : null, addressProps.sectionPrefix)
});
}
return (
<Select.Async
onChange={handleSuburbChange}
/>
)
}}
);
}
变量之间会出现命名冲突,因此我调用主要道具props
来避免这种情况。< / p>