简体   繁体   中英

How to initialize standalone Radio Button Group component checked property state with redux \ redux-form

I'm learning react and I am stuck. I am trying to share a form between Edit and Create components. I am having a really hard time figuring out what I am doing wrong to initialize the values of the checked property for the radio group. After the page loads I can toggle the radio buttons. Initially, however, neither is checked.

I'm starting to think I am setting up my checked property incorrectly on the form.

Edit component (forms parent component):

class CompanyMasterEdit extends React.Component {
  componentDidMount() {this.props.fetchCompany(this.props.match.params.companyId,this.props.match.params.companyName)}

  onSubmit = (formValues) => {    this.props.editCompany(this.props.match.params.companyId, formValues)   }
  render() {
    return (
      <div style={{ padding: 50 }}>
        <h1 style={{ textAlign: "center" }}>Edit Company Master Record</h1>
        <h5 style={{ textAlign: "center" }}>
          <span style={{ color: "red" }}>*</span> indicates required field
        </h5>
        <CompanyMasterForm
          initialValues={_.pick(
            this.props.company,
            "isKeyCompany",...
          )}
          onSubmit={this.onSubmit}
        />
const mapStateToProps = (state, ownProps) => {
  return { company: state.companies[ownProps.match.params.companyId] }
}
export default connect(mapStateToProps, { fetchCompany, editCompany })(  CompanyMasterEdit)

Form:

class CompanyMasterForm extends React.Component {
    
 <form
            className="ui form error"
            onSubmit={this.props.handleSubmit(this.onSubmit)}
          >
             <Field
              label="Is This a Key Company?"
              component={RadioGroup}
              name="isKeyCompany"
              required={true}
              options={[
                { title: "Yes", value: true },
                { title: "No", value: false },
              ]}
              checked={this.props.initialValues.isKeyCompany}
              // onClick={this.setIsKeyCompany}
              //onClick={(e) => this.setState({ isKeyCompany: e.target.checked })}
              onClick={() => {this.setState((prevState) => {return {checked: !this.props.initialValues.isKeyCompany,}})
              }}
            />
            <button className="ui button primary">Submit</button>
          </form>

RadioButtonGroup as a separate component:

    class RadioGroup extends React.Component {
    
      render() {
    
      return (
          <div className={className}>
            <div className="inline fields">
              <div className={labelClassName}>
                <label>{label}</label>
              </div>
            {options.map((o) => {
            const isChecked = o.value ? "true" : "false"
            console.log("o.value :", o.value)
            return (
              <label key={o.title}>
                <input
                  type="radio"
                  {...input}
                  value={o.value}
                  checked={isChecked === input.value}
                />
                {o.title}
              </label>
            )
          })}
            </div>
          </div>
        )
      }
    }
    // const mapStateToProps = (state) => {
    //   console.log("radio group state : ", state)
    //   return { isKeyCompany: state.form.companyMasterForm.values.isKeyCompany }
    // }
    // export default connect(mapStateToProps)(RadioGroup)
    // export default connect(null)(RadioGroup)
    export default RadioGroup

I have a codesandbox up. I feel like I'm close and dancing all around it but I can't get it. Any help is appreciated.

The value of an option is text (input.value is text but o.value is boolean). The checked property is boolean. === is never going to be quite right unless you fix one of those things.

This change makes an option show up. Not that it's truly the right change. We probably should have a slightly more robust compare for the checked property.

checked={o.value == input.value}

It's currently boolean because you set it here

options={[
            { title: "Yes", value: true },
            { title: "No", value: false },
          ]}

+1 on the research from @Nikki9696, but the better solution is to leave your comparison using the strict === check and change the options config to have string values:

options={[
   { title: "Yes", value: 'true' },
   { title: "No", value: 'false' },
]}

that's because "true" != true (the string value "true" is not equal to the boolean value true , even with type-coercion). The same can be said for a comparison between "false" and false .

A couple of remarks:

  • Radio buttons that are mutually exclusive should have the same name prop. So we can just write a hardcoded string for one set of options.
  • The value prop doesn't determine whether the radio button is active. This is done via checked / defaultChecked instead. The value prop is particularly useful if we have more than two options (to label each option).
  • It's not clear what the variable input is doing in the last code example?
  • It's always better to avoid "true" and "false" as string values, if there are only two possible values. In this case, a boolean ( true or false without the quotation marks) is more suitable. This is the simplest way to represent your state, and typos such as "True" won't slip through the cracks (when you're using strings, such typos will go unnoticed).
  • If you're using uncontrolled components (ie the value of an input is not kept in the component's state), you should use defaultChecked instead of checked , if you want the user to be able to toggle the checkbox after the page has loaded.
  • If you want to reuse the state of the checkbox elsewhere in your app, you'll have to use controlled components . In this case, setting the state should be done slightly differently as well (see below).

Your RadioGroup component will look as follows:

class RadioGroup extends React.Component {
  state = { isKeyCompany: true } // initial value

  render() {
    return (
      <div className={className}>
        <div className="inline fields">
          <div className={labelClassName}>
            {label /* note: not wrapped in `<label>...</label>`, because it’s not the description of one option, but rather of the entire set. */}
          </div>
          {options.map((o) => (
            <label key={o.title}>
              <input
                type="radio"
                name="isKeyCompany" // for example
                checked={this.state.isKeyCompany === o.value} // or passed down via props
                onChange={() => this.setState({ isKeyCompany: o.value }))
              />
              {o.title}
            </label>
          ))}
        </div>
      </div>
    )
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM