user1912935
user1912935

Reputation: 381

React child to parent communication

Am new to react developing a page with 3 UI cards like structure each card has 10 to 15 fields. At last has submit button, which will submit the page and summary will display in next page.

so developed 3 components for each card and one main component which will combine 3 components and submit button when a user clicks on submit I want state data of all 3 components how to do this in react.

Can I use a callback or react redux, which is the best implementation. please provide some example. Sample Code:

import React from 'react'
import { BrowserRouter as Router, Switch, Route, Link, withRouter } from 'react-router-dom'

import {store} from '../../index'
import '../../UX-UI-Components/desktop/css/commonUI.css'
import Card1 from './Card1'
import Card2 from './Card2'
import Card3 from './Card3'
import Button from '../../components/Button'
import { FormattedMessage } from 'react-intl'

class Main extends React.Component {

  render() {
     return (
    <div className="">
            <Card1 />
            <Card2 />
            <Card3 />           
            <Link to={'/Summary'}>
            <Button buttonType="primary" onClick = {this.submit} align="right">
                <label><FormattedMessage id="lbl_viewSummary"/></label>
            </Button>
            </Link>
            <Button buttonType="second" align="right">
                <label><FormattedMessage id="lbl-cancel"/></label>
            </Button>
    </div>
     );
  }
}
export default Main
===================================================================================
class Card1 extends React.Component {
  constructor(props) {
     super(props);
     this.state = {
        d1:"",
        d2: "",
        ......
        d10:""
     }
  };

  render() {
     return (
            <Text onChange={(event) => this.setState({ d1: event.target.value })}>   
            <Text onChange={(event) => this.setState({ d2: event.target.value })}> 
            .....
            <Text onChange={(event) => this.setState({ d10: event.target.value })}>   


     );
  }
}

export default Card1

like wise 2 more card classes.. i need all these cards states values in main class

Thank You

Upvotes: 1

Views: 7206

Answers (1)

TPHughes
TPHughes

Reputation: 1627

If your application is going to get much larger than this then redux is definitely the way to go, you should read in to it.

To do this without redux you would need to define all the fields in the state of the parent component and pass methods to the children to update them. So you could do something like this:

Parent

import Child from './Child';

class Parent extends React.Component {
    constructor(props) {
       super(props);

       this.state = {
           card_1: {
               field_1: null,
               field_2: null
           },
           card_2: {
               field_1: null,
               field_2: null
           },
           card_3: {
               field_1: null,
               field_2: null
           }
       }
    }

    updateField(card, field, value) {
        this.setState({
            ...this.state,
            [card]: {
                ...this.state[card],
                [field]: value
            }
        });
    }

    handleClick() {
        // Do something with this.state
    }

    render() {
            return (
                <div>
                    <Child {...this.state} updateField={(card, field, value) => this.updateField(card, field, value)} />
                    <button onClick={() => this.handleClick} />
                </div>
            )
    }
}

Child

class Child extends React.Component {
    render() {
            return (
                <input
                    name="field_1"
                    defaultValue={this.props.card_1.field_1}
                    onChange={(e) => this.props.updateField("card_1", "field_1", e.target.value)}
                />
            )
    }
}

This method updates the state in the Parent component, so you can then call a method with from an onClick in a button on that component and use the data in the state to process.

--- Edit ---

To do this using redux I would create a reducer to deal with the form, and an action to update the form data in the state. Something like this:

Files

-Form.js
-Card.js
-actions.js
-reducer.js

Form

import Card from './Card';
import { initialState } from './reducer';
import { formUpdateField } from './actions';

class Form extends React.Component {
    constructor(props) {
       super(props);

       this.state = { ...initialState } // Copy the initial state to our Component state when it loads
    }

    componentWillReceiveProps(newProps) {
        // Set the props to the component sate
        return this.setState({ ...newProps });
    }

    handleClick() {
        // Do something with this.state
        // Or better still, call to another action to deal with the state in the reducer directly
    }

    render() {
            return (
                <div>
                    // Populate each of the Card components and send the state along with the updateField action and the id for each card
                    <Card cardId='card_1' {...this.state} updateField={(card, field, value) => this.props.updateField(card, field, value)} />
                    <Card cardId='card_2' {...this.state} updateField={(card, field, value) => this.props.updateField(card, field, value)} />
                    <Card cardId='card_3' {...this.state} updateField={(card, field, value) => this.props.updateField(card, field, value)} />

                    // Button to click and use the handle the data in state
                    <button onClick={() => this.handleClick} />
                </div>
            )
    }
}

function mapStateToProps(state) {
  // Return the new state from the reducer to componentWillReceiveProps
  return { ...state.formReducer }
}

function mapDispatchToProps(dispatch) {
    // Map our actions to this.props using the store dispatch method
    return {
        updateField(card, field, value) => dispatch( formUpdateField(card, field, value) )
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(Form);

Card

class Card extends React.Component {
    render() {
        return (
                   <div>
                       {
                            Object.keys(this.props[ this.props.cardId ]).map(key => {
                                return <input
                                name={ key }
                                defaultValue={ this.props[card_1][key] }
                                onChange={(e) => this.props.updateField(this.props.cardId, key, e.target.value)}
                            />
                        }
                    </div>
                )
    }
}

actions

import store from '../store'; // or wherever your redux store is created

export const formUpdateField = ({ card, field, value }) => {
     let state = store.getState().formReducer;

     // Change field in state
     state = {
         ...state,
         [card]: {
             ...state[card],
             [field]: value
         }
     };

     return {
         type: FORM_UPDATE_FIELD,
         payload: state
     }
}

reducer

export const initialState = {
    card_1: {
        field_1: null,
        field_2: null
    },
    card_2: {
        field_1: null,
        field_2: null
    },
    card_3: {
        field_1: null,
        field_2: null
    }
};

export const FORM_UPDATE_FIELD = 'FORM_UPDATE_FIELD';

export const formReducer = (state = initialState, action) => {
    switch (action.type) {
        case FORM_UPDATE_FIELD:
            // Return new state from action to component
            return action.payload;

        default:
            // Always return a default state
            return state;
    }
};

Please note that this is pseudo code and untested.

Here is a good tutorial on using redux with react: https://www.valentinog.com/blog/react-redux-tutorial-beginners/

I hope this helps.

Upvotes: 4

Related Questions