React Redux - Uncaught TypeError:无法读取未定义的属性“setState”

[英]React Redux - Uncaught TypeError: Cannot read property 'setState' of undefined


New to this.

新。

I have looked for answers here and here.

我到处寻找答案。

am using Redux as well. As per good practice I have a container "AddressContainer" and its component "Address".

我也在使用Redux。根据良好的实践,我有一个容器“AddressContainer”及其组件“Address”。

The AddressContainer is as follows -

地址容器如下所示。

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)

The Address component is as follows -

地址组件如下所示

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

The problem is with the following function in the address component below and with setState which it says is undefined -

问题是下面地址组件中的下列函数和它说的未定义的setState

         const onSuburbChange = (value) => {
          console.log('onSuburbChange: ', value)
          this.setState({ selectedSuburb: value }, () => {
            input.onChange(value)
            updatePostcodeValue(value ? value.postcode : null, sectionPrefix)
          })
        }

You will note there is a console.log for "value". This produces the result:

您将注意到有一个控制台。“价值”的日志。由此产生的结果是:

onSuburbChange: Object {id: 6810, suburbName: "Eaglehawk", postcode: "3556", state: "VIC"}

{id: 6810,郊区:"Eaglehawk",邮编:"3556",state: "VIC"}

I am using React-Select as the async dropdown. This all works. If I select an option I get dropdown options but select one and it gives me the error.

我在异步下拉时使用的是“堆-选择”。这一切工作。如果我选择一个选项,我得到下拉选项,但是选择一个,它会给我错误。

I am using react state here for selectSuburb options as I dont need to update redux with this - just react state.

我在这里使用的是react state用于selectsuburban选项,因为我不需要用这个更新redux——只需使用react state即可。

It seems all right but I still get the error. Why am I getting this error and how do I fix it?

看起来没问题,但我还是得到了错误。为什么会出现这个错误,我该如何修正呢?

1 个解决方案

#1


1  

This specific error is caused by the fact that the <Address /> component is a stateless functional component and cannot have a this.state object or a setState function in it. However, more generally it looks like you are expecting state and functions from the <AddressContainer /> component to be available to the child <Address /> component, but that cannot happen. In this case, you are wanting to modify the state of the parent by calling setState on the child.

这个特定的错误是由于

组件是一个无状态函数组件,并且不能有这个。状态对象或其中的setState函数。然而,更一般的情况是,您希望 组件中的状态和函数对子
组件可用,但这是不可能的。在本例中,您希望通过调用子节点上的setState来修改父节点的状态。

A child React component (in this case <Address />) will only have state/functions/properties from its parent that are explicitly passed down as props to that component. If you want to change the local state of a component it must happen on the component with the local state. If you want to have a child component trigger some type of function call on the parent, then that function must be passed down as a prop to the child and the child can call it.

子React组件(在本例中为

)将只具有其父组件的状态/函数/属性,这些属性作为道具被显式地传递给该组件。如果要更改组件的本地状态,则必须在组件上使用本地状态。如果您想让子组件在父组件上触发某种类型的函数调用,那么该函数必须作为一个道具传递给子组件,子组件可以调用它。

If I understand your code correctly, you want 3 things to happen when the Suburbs FormField is changed, in this order.

如果我正确地理解了您的代码,您希望在郊区FormField发生三件事情,按这个顺序。

  1. The selectedSuburb state on <AddressContainer /> is updated.
  2. 更新 上的selectedsuburban state。
  3. The onChange of the Redux-Form <Field /> in <Address /> is triggered.
  4. 中,Redux-Form 的onChange被触发。
  5. The updatePostCode action is fired off.
  6. 触发updatePostCode动作。

If that is correct, then you will need to move your onSuburbChange to the <AddressContainer /> and pass it to <Address /> as a prop. However, you cannot call the onChange of the Redux-Form <Field /> inside <AddressContainer />. Therefore, you can make that function expect to receive a callback function that will be fired off after the state updates. Then you can define the callback in the child component but the state-changing function in the parent. As long as you pass down needed props on the parent, such as updatePostCode and sectionPrefix, you'll be golden. Here's what it would look like:

如果这是正确的,那么您将需要将您的on郊区更改移动到 ,并将其传递到

作为一个支柱。但是,不能在 中调用Redux-Form 的onChange。因此,您可以使该函数期望接收回调函数,该函数将在状态更新后触发。然后可以在子组件中定义回调,而在父组件中定义状态更改函数。只要您将所需的小工具传递给父类,比如updatePostCode和sectionPrefix,您就会得到好处。它是这样的:

AddressContainer

AddressContainer

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}
      />
    );
  }
}

Address

地址

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}
          />
        )
      }}
  );
}

As you can see, there is going to be a naming conflict between the different props variables in the <Address /> component, so I call the main props addressProps to avoid this.

如您所见,在

组件中,不同的道具变量之间会有命名冲突,因此我调用主道具addressProps来避免此冲突。


注意!

本站翻译的文章,版权归属于本站,未经许可禁止转摘,转摘请注明本文地址:https://www.itdaan.com/blog/2017/06/10/1538552b5c1401842c5d0ef47d7cbdd4.html



 
  © 2014-2022 ITdaan.com 联系我们: