Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to render a component on button click in React?

Tags:

reactjs

I'm using React and Material UI for a project, and I'm implementing payment methods. I will basically support different type of payment methods, and depending which one user chooses I want to display a form, and once the user fills the form and submits it, I want to return to my previous view. I have a component that looks like this:

import React, { Component } from 'react';
import { List, ListItem } from 'material-ui';
import { Button } from '../../components';
import StripeForm from './stripe/form';
import PropTypes from 'prop-types';

class MyComp extends Component {
    constructor(props) {
        super(props);

        this.state = {
             paymentProviders: [],
             indexOfClickedItem: -1,
        };

        this.onListItemClicked = this.onListItemClicked.bind(this);
        this.onPayment = this.onPayment.bind(this);
    }

    onListItemClicked(paymentProvider, index) {
        let paymentProviders = {
            providerName: paymentProvider.ProviderName,
            publicKey: paymentProvider.PublicKey,
        };
        this.setState({
            paymentProviders: paymentProviders,
            indexOfClickedItem: index,
        });
    }

    onPayment() {
        switch (this.state.paymentProviders.providerName) {
             case "stripe":
                 // render the form for stripe payment
                 return ( <StripeForm /> );

             case "paypal":
                 // render the form for paypal payment

             default:
                 return null;
         }
    }

    render() {
        return (
            <div>
                <div>
                    <List>
                        {this.props.paymentProviders.map((pp, index) => {
                            return (
                                <ListItem key={pp.ProviderName}
                                        primaryText={pp.ProviderName}
                                        onClick={() => this.onListItemClicked(pp, index)}
                                />
                            )
                        })}
                    </List>
                </div>

                <div>
                    <Button onClick={this.onPayment}
                            label="Pay" />
                </div>
            </div>
        );
    }
}

MyComp.propTypes = {
    paymentProviders: PropTypes.array.isRequired,
};

export default MyComp;

So, basically, depending on which ListItem is clicked I update my indexOfClickedItem in the state, and then also update some details about the payment provider. But, what I mainly want to achieve is that, when my button is clicked, depending on which payment provider is chosen (meaning which list is previously clicked/selected), I want to render another component. Any ideas how to achieve it?

like image 594
typos Avatar asked Oct 16 '17 16:10

typos


2 Answers

You can use conditional rendering.

First initialize a variable inside constructor to store the form, and add a state viewForm to manage whether to view the form or not:

constructor(props) {
    super(props);

    this.state = {
         paymentProviders: [],
         indexOfClickedItem: -1,
         viewForm: false
    };

    this.onListItemClicked = this.onListItemClicked.bind(this);
    this.onPayment = this.onPayment.bind(this);
    this.paymentForm = null;  //this
}

On button click, conditionally assign a value to it when a payment mode is selected, and change the state to view the form:

    switch (this.state.paymentProviders.providerName) {
         case "stripe":
             this.paymentForm = <StripeForm/>
             break;
         case "paypal":
             this.paymentForm = <PaypalForm/>
             break;
         default:
              this.paymentForm = null;
     }
     this.setState({ viewForm: true });

.. and then render it conditionally:

<div className='form-container'>
  {(this.state.viewForm) ?
  this.paymentForm : ''}
</div>
like image 184
Dane Avatar answered Sep 20 '22 13:09

Dane


just add a conditional to your render method which examines state and renders the new component if the conditions are right:

render() {
    // render payment provider component for selected provider
    const {paymentProviders, indexOfClickedItem, paymentClicked} = this.state;
    const selectedPP = paymentProviders && paymentProviders[indexOfClickedItem];
    if (paymentClicked) {
        // whatever mechanism you want to use to decide
        // what to render for this payment provider...
        const ComponentToRender = selectedPP.Component;
        return (
          <ComponentToRender
            // unselects the item when the user finishes the payment
            onComplete={() => this.setState({indexOfClickedItem: -1})}
            prop1={"foo"}
          />
        );
    }

    // if nothing selected, render list
    return (
        <div>
            <div>
                <List>
                    {this.props.paymentProviders.map((pp, index) => {
                        return (
                            <ListItem key={pp.ProviderName}
                                    primaryText={pp.ProviderName}
                                    onClick={() => this.onListItemClicked(pp, index)}
                            />
                        )
                    })}
                </List>
            </div>

            <div>
                <Button onClick={this.onPayment}
                        label="Pay" />
            </div>
        </div>
    );
}

Your onPayment function should issue a this.setState({paymentClicked: true}) to trigger the new render path.

like image 21
Brandon Avatar answered Sep 22 '22 13:09

Brandon