Manipulating the visibility of components by toggling on click events according to the existing state in ReactJS

Disclosure: I am still getting familiar with ReactJS

I'm currently working on incorporating a dialog window for selecting a country/language on the initial page of my application.

Here's the concept:

  • There's a small flag button in the top right corner that users can click (CountryFlag)
  • Upon clicking, a dialog box (Dialog) pops up displaying 5 countries (flags), each with 2 language options. Choosing a country (e.g. LA) and language (e.g. lo) results in a specific locale, like lo-LA.

Initially, my dialog box looks like this:

https://i.stack.imgur.com/BQ5GZ.png

After clicking on the Vietnamese flag, it is expected to change to this:

https://i.stack.imgur.com/ZV3rb.png

When the country flag is clicked, I intend for two buttons (divs) to appear at the right side of the flag for selecting a language. To achieve this, I am trying to conditionally add the divs based on the current state associated with the selected language:

<div className="Country-flag-big" onClick={this.selectCountry("KH")} data-country={"KH"} />
{ this.state.countrySelected==="KH" ? <div className="Language-big" onClick={this.selectLocale} data-locale={"km-KH"} >ភាសាខ្មែរ</div> : null }
{ this.state.countrySelected==="KH" ? <div className="Language-big" onClick={this.selectLocale} data-locale={"en-KH"} >English</div> : null }

However, there seems to be an issue as the dialog box opens, almost as if the onClick event has already been triggered and causing a conflict:

Warning: setState(...): Cannot update during an existing state transition (such as within render or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to componentWillMount.

Below is the complete component code:

import React from 'react';
import ReactDOM from 'react-dom';
import './CountryFlag.css';

var Dialog = React.createClass({
    getInitialState: function () {
        return { countrySelected: "" };
    },

    close(){
        ReactDOM.unmountComponentAtNode(this.props.container);
    },

    componentWillUnmount() {
        document.body.removeChild(this.props.container); 
    },

    selectCountry(country) {
        console.log('this is c:', country);
        this.setState({countrySelected: country});
    },

    selectLocale() {
        console.log('this is:', this);
        ReactDOM.unmountComponentAtNode(this.props.container);
    },

    render(){
        return(
            <div className="Country-dialog-overlay">
                <div className="Country-dialog-inner">
                    <h2>Country > Language</h2>
                    <div className="Country-flag-big" onClick={this.selectCountry("KH")} data-country={"KH"} />
                    { this.state.countrySelected==="KH" ? <div className="Language-big" onClick={this.selectLocale} data-locale={"km-KH"} >ភាសាខ្មែរ</div> : null }
                    { this.state.countrySelected==="KH" ? <div className="Language-big" onClick={this.selectLocale} data-locale={"en-KH"} >English</div> : null }

                    <div className="Country-flag-big" onClick={this.selectCountry("LA")} data-country={"LA"} />
                    { this.state.countrySelected==="LA" ? <div className="Language-big" onClick={this.close} data-locale={"lo-LA"} >ພາສາລາວ</div> : null }
                    { this.state.countrySelected==="LA" ?<div className="Language-big" onClick={this.close} data-locale={"en-LA"} >English</div> : null }

                    <div className="Country-flag-big" onClick={this.selectCountry("MM")} data-country={"MM"} />
                    { this.state.countrySelected==="MM" ? <div className="Language-big" onClick={this.close} data-locale={"my-MM"} >မြန်မာ</div> : null }
                    { this.state.countrySelected==="MM" ? <div className="Language-big" onClick={this.close} data-locale={"en-MM"} >English</div> : null }

                    <div className="Country-flag-big" onClick={this.selectCountry("TH")} data-country={"TH"} />
                    { this.state.countrySelected==="TH" ? <div className="Language-big" onClick={this.close} data-locale={"th-TH"} >ภาษาไทย</div> : null }
                    { this.state.countrySelected==="TH" ? <div className="Language-big" onClick={this.close} data-locale={"en-TH"} >English</div> : null }

                    <div className="Country-flag-big" onClick={this.selectCountry("VN")} data-country={"VN"} />
                    { this.state.countrySelected==="VN" ? <div className="Language-big" onClick={this.close} data-locale={"vi-VN"} >Tiếng việt</div> : null }
                    { this.state.countrySelected==="VN" ? <div className="Language-big" onClick={this.close} data-locale={"en-VN"} >English</div> : null }
                </div>
            </div>
        );  
    }
});

var Trigger = () => {
    function showDialog() {
        var div = document.createElement('div');
        ReactDOM.render(
            <Dialog container={div}/>,
            document.body.appendChild(div)
        );
    }

    return (
        <div className="Country-flag" onClick={showDialog} data-country={"VN"} />
    );
};

class CountryFlag  extends React.Component {
    render() {
        return (
            <Trigger />
        );
    }
}

export default CountryFlag;

Any insights into why this error is occurring?

Appreciate your help!

Answer №1

When you click on an element and have

onClick={this.selectCountry("KH")}

You are invoking the function immediately, rather than waiting for the click event. To ensure it only fires when clicked, you can use

onClick={() => this.selectCountry("KH")}

or alternatively, create a bound function that includes the country name, like this:

onClick={this.selectCountry.bind(this, "KH")}

Alternatively, if your elements have data- attributes containing relevant information, you can simplify by using just onClick={this.selectCountry}. In the selectCountry function, you can access the data attribute value like so:

selectCountry(e) {
  let country = e.currentTarget.dataset.country;
  // country will be the specified value of the data attribute
}

Answer №2

<div className="Country-flag-big" onClick={this.selectCountry("KH")} data-country={"KH"} />
code is causing the error.

The correct format should be:

<div className="Country-flag-big" onClick={() => this.selectCountry("KH")} data-country={"KH"} />

at every instance

The issue in your case is that onClick requires a function, but with

onClick={this.selectCountry("KH")}
, you are actually passing it a value returned by the selectCountry function. Since you are updating a state in selectCountry, the render method is called again and every time render is triggered, selectCountry is evaluated to return the value for onClick. To resolve this, you need to bind the function before passing the value.

Update your render function as follows:

render(){
    return(
        <div className="Country-dialog-overlay">
            <div className="Country-dialog-inner">
                <h2>Country > Language</h2>
                <div className="Country-flag-big" onClick={() => this.selectCountry("KH")} data-country={"KH"} />
                { this.state.countrySelected==="KH" ? <div className="Language-big" onClick={this.selectLocale} data-locale={"km-KH"} >ភាសាខ្មែរ</div> : null }
                // Add more similar blocks for other countries

            </div>
        </div>
    );  
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Craft a FormType showcase using the dynamic duo of Ajax and Jquery

If you'd like to check out the code snippet for reference, please visit: This view serves as the canvas where innovation blends with execution. As I embark on this journey towards dynamic form creation, every step counts. Thank you for being part of ...

Altering the text and functionality of buttons with jQuery

A JavaScript method I have is designed to change based on the state of a button, which is determined by using indexOf("..some text.."). $('#add1').click(function(){ if($(this).text().indexOf("Add Me!")) { $.ajax({ type: & ...

Is there a combination of 'auto' and 'none' values in any CSS properties?

Is it safe to assume that if a property is set to auto, it cannot have the value of none, and vice versa? Or if a property has none, can it not have auto as well? I understand that these values have distinct meanings, but I am curious if this concept has ...

Button ng-click with identical function parameters

I am facing an issue with two buttons that have the same ng-click but different parameters. <label class="item item-input"> <button ng-click="takePicture(true)">Save Settings</button> <button ng-click="takePicture(false)">Choos ...

Tips for centering elements in an image caption

I need some help with separating an image and text within an image caption from each other. Currently, they are overlapping and I want them to be distinct. I attempted using inner div elements for the text and image separately, but it caused issues by brea ...

WebStorm not recognizing NodeJS Core Modules in External Libraries

As a newcomer to NodeJS and WebStorm, I am currently diving into a tutorial on creating an Express app. In the tutorial, the instructor always gets prompted with "Configure NodeJS Core Module Sources" including the URL nodeJS.org when creating a new NodeJ ...

Is Passport.js' serializeUser and deserializeUser functions never triggering?

Encountering an issue with Passport-local. It seems that neither serializeuser nor deserializeUser are being invoked. Upon researching similar problems on SO, it appears that many others facing this problem were not properly including bodyParser. Below is ...

Which is better for scrolling in Angular 2+: using HostListener or window.pageYOffset?

Which syntax is best for maximizing performance in Angular 2+? Is it necessary to use HostListener, or is it simpler to obtain the scroll position using the onscroll window event and pageYOffset? @HostListener('window:scroll', ['$event&ap ...

Leveraging react-redux-firebase for integrating cloud functions into React JS applications

I recently started learning React JS and I'm working on a project where I need to display data from cloud firestore. To fetch the data, I am utilizing react-redux-firebase. It's all working smoothly so far. However, now I want to switch to retrie ...

How can Javascript split main.js into two separate files using import or require?

Currently, my main.js file is functioning perfectly despite its length. However, I am interested in organizing my code by separating the functions related to 'y' into a separate file. In PHP, this process can be easily achieved with require(&apos ...

Utilize Jquery to calculate the total sum of values associated with a particular key in a JSON object based on input

I am facing an issue with my script where I am trying to sum up the clientPrice keys in a JSON object assigned to a form text element. Here is what I have: <input id="clientList" type="hidden" value="[{"clientID":"1","clientType":"0","clientPrice":"450 ...

The text alignment in the Material-UI Paper component is not centralized

Hello everyone, I am new to React Material-UI and I'm facing an issue with the paper component. The text within the paper does not center properly when the height is low. Can someone provide guidance on how to fix this? If you check out the sandbox l ...

I encountered an issue where useRouter is returning null and router.isready is not functioning properly with nextjs version 13

After experimenting with Next.js version 13, I encountered an error in my code that I am struggling to resolve. Here is the code snippet: import { useRouter } from 'next/navigation'; async function getCheckoutInfo() { const router = useRoute ...

Move router parameters to separate files to streamline and organize code

I have encountered a bit of an issue. I currently have the following code in my routing.js file where I define both my parameter and route. I have moved the routes to a separate router instance in my routing.js file, but I am struggling to separate the par ...

Having trouble with Isomorphic fetch not functioning properly for external requests?

EDIT: I am trying to determine why the response does not include the requested data and whether it is due to missing libraries or the format of my fetchUrl variable. Hello, I am attempting to utilize the isomorphic fetch method for making AJAX requests bu ...

JavaScript - Retrieve a nested property within an object using an array of strings

I possess an object in the following format { metadata: { correlationId: 'b24e9f21-6977-4553-abc7-416f8ed2da2d',   createdDateTime: '2021-06-15T16:46:24.247Z' } } and I possess an array containing the properties I wish to re ...

having difficulty applying a border to the popup modal

Currently, I am utilizing a Popup modal component provided by the reactjs-popup library. export default () => ( <Popup trigger={<button className="button"> Guide </button>} modal nested > {(close: any) =&g ...

What is the process for incorporating a global package into the package.json file?

I am wanting to include redux-thunk in my project's package.json, but utilize the version installed globally (not within the specific node_modules directory). Should I manually add it to my package.json? When using npm install -g redux-thunk, the pack ...

Word.js alternative for document files

I'm on the lookout for a JavaScript library that can handle Word Documents (.doc and .docx) like pdf.js. Any recommendations? UPDATE: Just discovered an intriguing library called DOCX.js, but I'm in search of something with a bit more sophistic ...

Tips on personalizing the vue-filemanager interface within Laravel

I'm currently utilizing the Laravel file manager package from here, which provides a pre-compiled JS file or allows for direct use of the vue-component through npm from here. Unfortunately, in both options, the front-end is not customizable. I have i ...