Ensuring React state is updated accurately

What is the best approach to update the state in React efficiently? I have a dropdown menu with a list of countries and a table that displays the selected country along with its cities. The table should dynamically update based on the user's selection from the dropdown.

The code below successfully filters the table data for the initially selected country. However, it fails to display the correct data when the country is changed for the second time.

import React, { Component } from "react";
import logo from "./logo.svg";
import "./App.css";

import { makeStyles } from "@material-ui/core/styles";
import Button from "@material-ui/core/Button";

import MenuItem from "@material-ui/core/MenuItem";
import Select from "@material-ui/core/Select";

import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";

const useStyles = makeStyles(theme => ({
  button: {
    margin: theme.spacing(1)
  },
  input: {
    display: "none"
  }
}));

class App extends Component {
  state = {
    selectedCountry: "",
    countries: []
  };

  constructor() {
    super();

    this.state.countries = [
      this.createData("US", "DC"),
      this.createData("UK", "London"),
      this.createData("Aus", "Canberra"),
      this.createData("US", "Newyork"),
      this.createData("UK", "Birmingham"),
      this.createData("Aus", "Sidney")
    ];
  }

  createData(country, capital) {
    return { country, capital };
  }

  handleChange(value) {
    this.setState({ selected: value });
    this.setState({
      countries: this.state.countries.filter(c => c.country === value)
    });
  }

  render() {
    return (
      <div className="App">
        <label value="Select Country: ">Select Country: </label>
        <Select
          style={{ width: "10%" }}
          value={this.state.selected}
          onChange={event => this.handleChange(event.target.value)}
          name="country"
          displayEmpty
        >
          <MenuItem value="" disabled>
            Select a country
          </MenuItem>
          <MenuItem value="US">US</MenuItem>
          <MenuItem value="UK">UK</MenuItem>
          <MenuItem value="AUS">Aus</MenuItem>
        </Select>

        <Table>
          <TableHead>
            <TableRow>
              <TableCell align="center">Country</TableCell>
              <TableCell align="center">Capital city</TableCell>
            </TableRow>
          </TableHead>
          <TableBody>
            {this.state.countries.map(row => (
              <TableRow key={row.name}>
                <TableCell align="center">{row.country}</TableCell>
                <TableCell align="center">{row.capital}</TableCell>
              </TableRow>
            ))}
          </TableBody>
        </Table>
      </div>
    );
  }
}

export default App;

Answer №1

The issue lies right here,

this.setState({
   countries: this.state.countries.filter(c => c.country === value)
});

By filtering the countries array and storing it back to the same variable, you are essentially overwriting your original array with the filtered data. Subsequent filters then work on this modified array instead of the original one.

To solve this problem, maintain a copy of the original data in the constructor method,

constructor() {
    super();
    //Initial state
    this.state.countries = [
      this.createData("US", "DC"),
      this.createData("UK", "London"),
      this.createData("Aus", "Canberra"),
      this.createData("US", "Newyork"),
      this.createData("UK", "Birmingham"),
      this.createData("Aus", "Sidney")
    ];

    //Copy of original data
    this.countries = [
      this.createData("US", "DC"),
      this.createData("UK", "London"),
      this.createData("Aus", "Canberra"),
      this.createData("US", "Newyork"),
      this.createData("UK", "Birmingham"),
      this.createData("Aus", "Sidney")
    ];
  }

Now you can filter over the original data each time by referring to the copied array,

this.setState({
   countries: this.countries.filter(c => c.country === value)
});

Check out the Demo

Note: It's recommended to use .toLowerCase() when comparing values in your filter function. Since there may be case variations (e.g., Aus vs.

AUS</code), converting both sides to lowercase ensures accurate comparisons.</p>

<pre><code>this.setState({
   countries: this.countries.filter(c => c.country.toLowerCase() === value.toLowerCase())
});

Answer №2

Utilize a constant value within the render method to ensure it updates each time there is a change in the selected country.

Omit the following line of code:

this.setState({
      countries: this.state.countries.filter(c => c.country === value)
    });

Include the code below:

render() {
    const filteredCountries = this.state.countries.filter(c=> c.countries === this.state.selected);
    return (
      <div className="App">
        <label value="Select Country: ">Select Country: </label>
        <Select
          style={{ width: "10%" }}
          value={this.state.selected}
          onChange={event => this.handleChange(event.target.value)}
          name="country"
          displayEmpty
        >
          <MenuItem value="" disabled>
            Select a country
          </MenuItem>
          <MenuItem value="US">US</MenuItem>
          <MenuItem value="UK">UK</MenuItem>
          <MenuItem value="AUS">Aus</MenuItem>
        </Select>

        <Table>
          <TableHead>
            <TableRow>
              <TableCell align="center">Country</TableCell>
              <TableCell align="center">Capital city</TableCell>
            </TableRow>
          </TableHead>
          <TableBody>
            {this.state.filteredCountries.map(row => (
              <TableRow key={row.name}>
                <TableCell align="center">{row.country}</TableCell>
                <TableCell align="center">{row.capital}</TableCell>
              </TableRow>
            ))}
          </TableBody>
        </Table>
      </div>
    );
  }

Opt for this more efficient approach by using the existing state instead of creating another one for "filteredCountries".

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

Leveraging split and map functions within JSX code

const array = ['name', 'contact number'] const Application = () => ( <div style={styles}> Unable to display Add name & contact, encountering issues with splitting the array). </div> ); I'm facing difficul ...

Tips for extracting key values from an array of objects in Typescript

I am working with an array called studyTypes: const studyTypes = [ { value: "ENG", label: "ENG-RU", }, { value: "RU", label: "RU-ENG", }, ]; Additionally, I have a state variable set ...

Issue arises when isomorphic-dompurify is used alongside dompurify in Next.js 13 causing compatibility problems

I am currently facing a compatibility problem involving isomorphic-dompurify and dompurify in my Next.js 13 project. It appears that both libraries are incompatible due to their dependencies on canvas, and I am struggling to find a suitable alternative. M ...

Ways to dynamically retrieve a key value pair in JavaScript and React

I am currently working with a spreadsheet element where the cell values are stored in an object structure like this: localCells = {A1: {input: 'hi', value: 'world'}, A2: {input:'how', value:'you?'}} The object is q ...

In NextJS, where is the best place to run sensitive code to ensure it is only executed server-side?

I'm currently exploring the world of NextJS and I am in the process of figuring out how to structure my project with a solid architecture that prioritizes security. However, I find myself at a crossroads when it comes to determining the best place to ...

Rendering external HTML content within a React component can be achieved by utilizing parsing techniques

In my React application, I have a component that receives HTML content as a string field in the props from an API call. My objectives are: To render this HTML content with styles applied. To parse the content and detect "accept-comments" tags within sec ...

In Next.js 14, the cache cookie is obtained when navigating to pages, but only if there is no hard refresh

I am currently in the process of developing a small booking application using Next.js 14 without implementing any authentication. Within this app, there is a page called /events that displays a list of events, each with a "Book" button that triggers a func ...

The child component in react is not updating to reflect the changes in the state

I am currently facing an issue where I have two child components updating the same state of a parent component. Both child components contain a loop, and each element within the loop updates the state on click through an event. The Parent Component:- cons ...

Using React-Router-Native to send an image as a parameter

I am encountering an issue while attempting to pass an image as a parameter in react-router-native and retrieve the data from location.state. Typically, I use the following code to display an image: import Icon from '../image/icon.png'; <Vie ...

I'm encountering an authentication issue while passing an axios configuration object containing an authorization token. Can anyone provide insight on why this is happening? Using

I am currently developing a small social networking application and have implemented a feature for users to like posts. To ensure security, I have set up an authentication middleware as shown below: const auth = async (req, res, next) => { // check he ...

Placing an Image in Next.js

I've been trying to position my image all the way to the right of the page using CSS properties like position: absolute and right: 0. I've also attempted setting the parent element's position to relative and the image's position to abso ...

Is it feasible to execute rendering while performing a Drag and Drop operation in ReactJs?

Can ReactJS handle rendering during a Drag/Drop operation? I'm looking to create a month view calendar in ReactJS where, when dragging a multi-day event backwards and forwards, all other day events on the calendar also move to show the effect before ...

Deactivate automatic logins following a password reset with the clerk tool

Looking for help with implementing a "forgot password" feature in my Next.js project using Clerk. I've been following the official documentation provided by Clerk, but ran into an issue where users are automatically logged in after resetting their pas ...

Incorporate an SVG div into a React component

I have integrated an NPM package that utilizes D3 to draw a fretboard (https://github.com/txels/fretboard). The output is a div containing SVG elements. While I successfully implemented it on a regular HTML page, I encountered issues when attempting to loa ...

Learn how to use React to conditionally update or add objects within an array of objects by utilizing the setState method

I am working with a state variable called subscriptions, which is an array of objects. Whenever I receive a websocket message, I extract the values for symbol and price. My goal is to update the subscriptions array by either modifying the object that mat ...

The reasons behind cancellations in API requests

Within the parent component, the open and setOpen states were passed down to the child component. The fetchData function included open as a dependency in the useEffect hook. In the child component, open was also added as a dependency for another fetchDat ...

How can you refresh the information shown in a separate component from the search input with a live search bar?

Currently, I am working on integrating a live search functionality into my Next.js application. While I have successfully managed to capture input changes, I am facing difficulties in filtering the results based on the user input. Here is a snippet of the ...

Optimizing performance by implementing reducers from other components with redux-toolkit in the React boilerplate is a recommended

Although I primarily work with Python, I recently delved into the world of React. The rapid evolution of the React ecosystem is quite astonishing - taking a break for just a few months leaves me feeling like a complete novice again :) Currently, I am work ...

Making an Axios request upon clicking a button

Why is the functionality of deleting a quiz from the database not working as expected in the code snippet below, even though it works fine with Postman? deleteQuiz = () => { const quiz = this.state.quizData._id axios.delete(`http://localhost: ...

Title: Troubleshooting Nest.js Google OAuth Redirection Problem with CORS Blocking

I am currently encountering an issue with my Nest.js application that utilizes Google OAuth for authentication. The backend of Nest.js is running on port 5000, while the frontend (Next.js) is running on port 3000. I have properly configured the Google Clou ...