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

Displaying complex JSON data in an HTML format using JavaScript

How can I convert the second array of entities into an HTML format from the JSON data using a for loop or similar method? To access the necessary data, I currently use console.log(data.response[0].entities.urls); $(document).ready(function () { $.getJSO ...

Rearrange the items in an array that contain different data types

After spending some time searching on SO, I did find some helpful information but I am still struggling to achieve the desired solution. My current issue involves a keypad with numbers 1 through 5. When a number key is selected, it gets added to an array n ...

Vue components are failing to appear in the live environment, however they function perfectly in the development environment

My Laravel Vue project runs smoothly in development, but on the live shared hosting server, the vue components are not displaying. However, the Laravel views function correctly with no errors in the console. I have already run npm run production to minif ...

Vue's Global mixins causing repetitive fires

In an effort to modify page titles, I have developed a mixin using document.title and global mixins. The contents of my mixin file (title.ts) are as follows: import { Vue, Component } from 'vue-property-decorator' function getTitle(vm: any): s ...

I am encountering an IPFS error 403 Forbidden whenever I attempt to upload anything to the network. Do you think I should switch networks on my Metamask account?

I am currently working on a project and looking to incorporate ipfs-http-client. I have set up my project on infura and obtained my project ID along with the project secret key. Here is how I added it to my js file: I imported it using import { create as ...

The discrepancy in the array leads to a result of either 1 or an undetermined

Array x = [3,5,7,9,1] Array y = [3,7,8] z = x - y this should result in z = [5,9,1] (7 is common but I want it excluded) Below is the code snippet: function arrayDifference(x, y) { var arr = []; var difference = []; for (var i = 0; i<x.length& ...

werkzeug.exceptions.BadRequestKeyError: 400 Bad Request: The server is unable to process the request sent by the browser or proxy. This error occurred in a Flask web application

Can someone guide me on troubleshooting this issue in Flask? I am still learning. Server running at (Press CTRL+C to exit) 127.0.0.1 - - [26/Jul/2020 11:19:45] "GET /predict HTTP/1.1" 500 - Traceback (most recent call last): raise exceptions. ...

Utilizing a lone web element per row on a responsive webpage: A guide

click here for imageCurrently using the latest version of selenium web driver. Is there a method to utilize a single webelement (as a list webelement) in order to access row data on a responsive web page? For instance, consider the following CSS in the br ...

What is the best way to hide the jQuery modal I created?

Hello everyone! Currently, I am working on coding a simple JS modal that can be opened and closed smoothly. The issue I am facing is related to adding a click function to (.black-overlay) in order to fade out everything and close the modal. <div class ...

Canvas does not display any results when trying to integrate Shadertoy

While browsing through StackOverflow, I came across a post detailing how to transfer shader examples from ShaderToy into Three.js. You can find the post here. I followed the steps mentioned in the post and created this Plunker demo. The fragment shader co ...

Add flair to the ButtonBase element within a Tab component

I'm currently exploring how to override Material UI styles with a nested component. For instance, what if I want to increase the bottom border height on an active Tab, which is applied by the underlying ButtonBase. Below is the style definition: con ...

What is the correct way to forcefully override an existing type in TypeScript?

As I work with Formik, a React form library, I find myself creating custom fields and utilizing the generic Schema type provided by Formik. This type represents the values object, which holds all the values for each field in the form. One of the custom co ...

Creating a real-time clock in a single line console log format using NodeJS

To create a live clock in a single line display within a browser, we can utilize setInterval and manipulate the content inside an HTML element like so: var span = document.getElementById('span'); function time() { var d = new Date ...

Adding Jade template variable to an inline function

Trying to implement the JSCharts library. block content | <div id="chartcontainer">This is just a replacement in case Javascript is not available or used for SEO purposes</div> script. var myData=new Array() var myData = new Array([10 ...

How to retrieve the same value from multiple selections using Vanilla JavaScript and multiple select options?

Why do we consistently receive the same value and index when using the ctl key to select multiple options? document.querySelector('select').addEventListener('change', function(e) { console.log(this.selectedIndex) console.log(e.ta ...

The dynamic import feature provides a different result of {$$typeof:...,render:f...} instead of the usual exported class

In my Next.js project, I am attempting to bring in the Quill module by using the following code: const Quill = dynamic(import("quill"), { ssr:false, }) However, when I log Quill, I am seeing a result of {$$typeof:...,render:f...} instead of the expect ...

Simply interested in extracting the JSON Class specifically, not all of the data

Looking for a way to extract only text from a specific class using $.getJSON and YQL. Currently, it retrieves all data and removes tags. Is there a method to achieve this? function filterData(data){ // remove unwanted elements // no body tags ...

Why is the table not sorting when I apply filters?

I am encountering an issue where the data filters and table sorting are not working together. When I apply filters, the sorting functionality stops working. The filters work fine independently, but once applied, they interfere with the sorting feature. Any ...

Error: Unable to access 'type' property of null value

I am trying to display an Alert in my React app, but I am facing issues with it. When I try to type {props.alert.type} and {props.alert.msg}, the code doesn't seem to work. I am stuck here and would appreciate any help in resolving this. import React, ...

The Next JS build process is failing to generate certain paths

Issue with Anime Database App Deployment A problem arose when I developed an anime database app using Nextjs and deployed it on Vercel. Although the build was successful and the initial page rendered properly, only a few dynamic routes displayed correctly ...