简体   繁体   中英

New to React: Why is one array treated differently than the other?

I'm working on a React app that is fed data from a Rails api. I'm currently working on a form that includes a nested association (ie in the model_a has many model_b's and you can create them in the same form).

The problem I'm having is that Rails expects nested association with a certain naming convention and the same field that controls how the parameter is named when its sent to rails also controls how React finds the right data when the Rails API responds.

This becomes problematic on the edit page because I want to show the models_a's ( Retailers ) already existing model_b's ( SpendingThresholds in this case) and when I change the 'name' field to suit the rails side, React doesn't know where to look for that data anymore. When I try to pass the data directly it comes in as a different type of array and certain functions fail.

I think its easier to show than tell here so

initially I had this

<FieldArray
  name="spending_thresholds"
  component={renderSpendingThresholds}
/>

and data was coming through like

Object {_isFieldArray: true, forEach: function, get: function, getAll: function, insert: function…

to my React app from the Rails API, which worked, however that 'name' isn't to Rails liking (Rails wants it to be called 'spending_thresholds_attributes' for accepts_nested_attributes to work) so I changed it to

<FieldArray
  name="spending_thresholds_attributes"
  fields={this.props.retailer.spending_thresholds}
  component={renderSpendingThresholds}
/>

and data start coming through to the renderSpendingThresholds component in this format

[Object]
  0:Object
  length:1
  __proto__:Array(0)

which React doesn't like for some reason.

Anyone know how to fix this/why those two objects, which hold the same information from the Rails side anyway, are being treated differently?

EDITS

renderSpendingThresholds component

The fields attribute in the renderSpendingThresholds component is the object that's coming through differently depending on how I input it

const renderSpendingThresholds = ({ fields }) => (
 <ul className="spending-thresholds">
    <li>
      <Button size="sm" color="secondary" onClick={(e) => {
          fields.push({});
          e.preventDefault();
        }
      }>
        Add Spending Threshold
      </Button>
    </li>
    {fields.map((spending_threshold, index) => (
      <li key={index}>
        <h4>Spending Threshold #{index + 1}</h4>
          <Button
            size="sm"
            color="danger"
            title="Remove Spending Threshold"
            onClick={() => fields.remove(index)}
          >
            Remove
          </Button>
        <Field
          name={`${spending_threshold}.spend_amount`}
          type="number"
          component={renderField}
          label="Spend Amount"
          placeholder="0"
        />
        <Field
          name={`${spending_threshold}.bonus_credits`}
          type="number"
          component={renderField}
          label="Bonus Credits"
          placeholder="0"
        />
      </li>
    ))}
  </ul>
);

It looks like you are passing fields through props and then destructuring the fields out of the props in the callback of the renderSpendingThresholds and discarding the rest. According to the docs, a specific redux-form object is passed through to the render callback. You're essentially overwriting this. Try changing {field} to something like member or spending_threshold . Then you can use the specific map function to iterate over the spending_threshold items. Your field prop should still be available under member.fields or something similar.

For the code that you currently show, who exactly handles the submission?

you use the original flow of form submit?

if so, so please handle that by yourself.

** this line of code, looks weird:

onClick={() => fields.remove(index)}

as you interact directly with the state values... you need to update the state through

this.setState({fields: FIELDS_WITHOUT_ITEM})

and now when you need to handle your own submission, you don't really care of the input names. Because you are using the state as input.

ie:

class FormSpending extends Component {

    handleSubmit() {
        var fieldsData = this.state.fields.map(field => {
            return {
                whateverkey: field.dontcare,
                otherKey: field.anotherDontCare
            };
        });
        var formData = {
             fields: fieldsData
        };

        ajaxLibrary.post(URL_HERE, formData).....
    }

    render() {
        return (
            ...
            <form onSubmit={()=>this.handleSubmit()}>
            ...
            </form>
            ...
        ); 
    }
}

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