2

I have a grandparent component that renders a card parent component by mapping over an array. Those parent cards have form children. The call to handle the submit function of the forms is in the grandparent. How can I know from which parent the form is coming?

I read this SO explainer about the difference between props and state several times, and I think it is possible that I somehow need to get to state instead of props, but when I console.log(this.state) in the handleSubmit function, it is null.

In my contrived example, I have a set of monkeys and a set of cupcakes. The cupcakes include a reference to the monkeys (stored in mongodb and my state), so that each cupcake belongs to one monkey. In order to create a new cupcake, I need to know which monkey it goes with.

I've tried several things, including passing the monkey in the form, adding a hidden field with the monkey (which seems very hackish to begin with and did not work). When I console.log(this.props) in the child form render() method, it shows that monkey is part of the props. However, monkey does not come through in the this.props that is passed through {handleSubmit}. I simply do not understand.

monkeys.js - the grandparent (the monkey cards are the parents) What I really want is for the handleCupcakeSubmit to include information about the monkey, so I can create a new cupcake for that monkey.

import React, { Component } from 'react';
import PropTypes from 'prop-types';

import MonkeysCupcakes from './monkeyscupcakes';
import AddCupcakeForm from './add_cupcake_form';

export default class Monkeys extends Component {
    constructor(props) {
        super(props);
        this.handleCupcakeSubmit = this.handleCupcakeSubmit.bind(this);
    }

    handleCupcakeSubmit({monkey, color }) {
        console.log("trying to submit a cupcake")
        console.log(this.state)
        console.log('handleSubmitCupcake with', monkey, color);
        // const userid = this.props.monkey.user;
        // const mid = this.props.monkey._id;
        // Eventually, I want to be able to have the userid and monkeyid - both of which are in the monkey props 
        //this.props.createCupcake({ userid, mid, color });
    }

    renderMonkeyCupcakes(mid) {
        if (
            this.props.cupcakes !== undefined &&
            this.props.cupcakes.length > 0
        ) {
            console.log("Monkeys is trying to render it's cupcakes");
            const mc = this.props.cupcakes.filter(
                cupcake => cupcake.monkeyid === mid
            );
            return <MonkeysCupcakes cupcakes={mc} />;
        } else {
            console.log('Monkeys has no cupcakes');
        }
    }
    render() {
        return (
            <div>
                {this.props.monkeys.map((monkey, i) => (
                    <div key={i} className="card blue-grey darken-1">
                        <div className="card-content white-text">
                            <span className="card-title">{monkey.name}</span>
                            <p>I am a monkey card</p>
                            <AddCupcakeForm monkey={monkey}
                                onSubmit={this.handleCupcakeSubmit}
                            />
                        </div>
                        {this.renderMonkeyCupcakes(monkey._id)}
                    </div>
                ))}
            </div>
        );
    }
}
Monkeys.propTypes = {
    monkeys: PropTypes.array,
    cupcakes: PropTypes.array
};

add_cupcake_form.js the child

import React, { Component } from 'react';
import { reduxForm, Field } from 'redux-form';
import { connect } from 'react-redux';

import renderTextField from '../helpers/textfield';
import { createCupcake } from '../../actions';

class AddCupcakeForm extends Component {


    render() {
        const { handleSubmit } = this.props; // no monkey
        console.log("add cupcake form props", this.props); // this.props.monkey exists
        return (
            <div className="section">
                <form onSubmit={handleSubmit}>
                    <Field
                        label="Color"
                        name="color"
                        placeholder="Purple"
                        component={renderTextField}
                        type="text"
                    />


                    <button className="btn-large" type="submit">
                        Add Cupcake
                        <i className="material-icons right">done</i>
                    </button>
                </form>
            </div>
        );
    }
}

const validate = values => {
    const errors = {};

    if (!values.color) {
        errors.color = 'Please enter cupcake color';
    }

    return errors;
};

export default reduxForm({
    form: 'addcupcake',
    validate
})(connect(null, { createCupcake })(AddCupcakeForm));

My project is on github, the branch with this issue is called connectcuptomonkeys which should be right here

jessi
  • 1,438
  • 1
  • 23
  • 36
  • https://redux-form.com/7.2.1/docs/gettingstarted.md/#basic-usage-guide the form is in rudex store. So if you set `mapStateToProps` with the same name as the example, it should be in `this.props.form` – FisNaN Mar 13 '18 at 04:24
  • I don't fully understand this. In a great-grandparent component (UserDashboard), I render the Monkeys. That great-grandparent has `mapStateToProps`, but I don't map state in the lower components - I'm passing props through. – jessi Mar 13 '18 at 05:00

1 Answers1

0

Option 1: send the monkey to the parent when you call handleSubmit:

    render() {
    const handleSubmit = () => this.props.handleSubmit({
      monkey: this.props.monkey
    });
    return (
        <div className="section">
            <form onSubmit={handleSubmit}>

Option 2: use a closure when you pass handleCupcakeSubmit:

 {this.props.monkeys.map((monkey, i) => {
    let handler = () => this.handleCupcakeSubmit({monkey})

    return (
                <div key={i} className="card blue-grey darken-1">
                    <div className="card-content white-text">
                        <span className="card-title">{monkey.name}</span>
                        <p>I am a monkey card</p>
                        <AddCupcakeForm monkey={monkey}
                            onSubmit={handler}
                        />
                    </div>
                    {this.renderMonkeyCupcakes(monkey._id)}
                </div>
            ))}
   }
Sylvain
  • 19,099
  • 23
  • 96
  • 145
  • With Option 1, I'm getting the whole application refresh when I hit submit. With Option 2, I'm getting both monkey and color undefined in the `handleCupcakeSubmit({ monkey, color }) { console.log('handleSubmitCupcake with', monkey, color); }` – jessi Mar 13 '18 at 05:29
  • I don't think the refresh is related to option 1 or option 2. you will have to investigate this further. I fixed the code for option 2. I had to pass an object with a `monkey` attribute to `handleCupcakeSubmit`, not the `monkey` value itself. – Sylvain Mar 13 '18 at 13:52