Toggle visibility of button based on React state

My current setup involves a button named download, which is a component integrated into my interface. The functionality associated with this button is implemented within a container.

I intend for the button to remain hidden by default unless a specific condition (i.e., "(i === number)" in the download function) is met.

The challenge lies in the fact that this condition can only be evaluated upon clicking the "download" button. This presents an obstacle as I am unsure how to preemptively validate this logic to determine whether the button should be displayed or not.

Could you provide assistance with resolving this issue? My attempt to establish a state variable for displaying the button has been unsuccessful.

The code snippet I have currently is as follows:

Container:

state = {
  showButton: false,
};

componentDidMount() {
  this.download();
};

download = async () => {
  const data = await this.props.client
      .query({
          query: numberQuery,
          fetchPolicy: "no-cache",
      })
  // retrieve data from number query ....
  const contentData = data.data.content;
 
  // retrieve and format numbers
  const numbers = this.getNumbers(contentData);
 
  // call get number and get the individual number here
  const number = await this.getNumber();
 
  numbers.forEach((i) => {
     // check if numbers contain the number from getNumber(), if number matches number in numbers
     if (i === number) {
       this.setState({
        showButton: true,
       });
          // call functions, start downloading
     };
   });
  };
 
 render() {
   return (
     {this.state.showButton ? 
      <Download 
      onStartDownload={() => this.download()}
      /> : null}
    );
 };

Component:

 class Download extends Component {
  state = {
    startDownload: false,
  };
 
  startDownload = () => {
    this.props.onStartDownload();
  };
 
  render() {
    return (
      <Fragment>
        <Button
          id="download"
          onClick={this.startDownload}
        >
          Download
        </Button>
      </Fragment>
     );
    };
  };

Answer №1

It seems like the issue is that the numbers are only fetched and the button logic is executed after the download button is clicked. You want this process to happen as soon as possible, preferably when the component mounts.

In React, it's a good practice to separate data fetching from render/event handling logic. In your case, one solution would be to fetch the data when the component mounts, store it in state variables like numbers and number, and then check if the number exists in the array when rendering the component.

Here is an example:

// Parent component; child component remains unchanged

state = {
  number: null,
  numbers: []
};

componentDidMount() {
  this.fetchNumbers();
  this.fetchNumber();
};

async fetchNumbers() {
  const data = await this.props.client
    .query({
      query: numberQuery,
      fetchPolicy: 'no-cache',
    });
  // retrieve data from number query ....
  const contentData = data.data.content;

  // retrieve and format numbers
  const numbers = this.getNumbers(contentData);

  this.setState({ numbers });
}

async fetchNumber() {
  // Assuming this involves another HTTP request or similar operation
  const number = await this.getNumber();

  this.setState({ number });
}

download = async () => {
  // Logic for downloading only
};
 
render() {
  const { number, numbers } = this.state;
  const showDownload = numbers.includes(number);

  return showDownload 
    ? <Download onStartDownload={() => this.download()}/> 
    : null;
 };
}

Some notes:

  • Using Array.includes() instead of an Array.forEach() loop simplifies the code and reduces potential bugs.
  • I separated the functions fetchNumbers() and fetchNumber() because they deal with two distinct pieces of state. It might be more efficient to fetch them independently. You could also improve it by having the functions return data instead of changing state, and then utilizing Promise.all() in componentDidMount().
  • It's a best practice in web development to indicate loading until core data asynchronously loads, using loaders, especially in production environments.

Answer №2

To ensure your class component works properly, make sure to include a constructor and utilize the setState() method to update the state. Take a look at the code snippet below for reference:

 class Download extends react.Component {
  constructor() {
    super(); // Make sure to include this line
    this.state = {
      startDownload: false
    };
  }

  startDownload = () => {
    this.setState((prevState) => ({
      startDownload: !prevState.startDownload
    }));
  };

  render() {
    return (
      <> 
        <button id="download" onClick={this.startDownload}>
          Download
        </button>
      </>
    );
  }
}

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

Accept forward slashes in route parameters on React

I have a similar router setup: <Route path="/blog/:date/:folder" render={(props: any) => <Home /> It functions well with URLs like this: http://localhost/blog/2017-05-20/test But, when :folder includes slashes (subdirectories), I want to ca ...

Having difficulty modifying the styling of a paragraph within a div container

I have been working on a function that is supposed to adjust the font-size and text-align properties of a paragraph located within a div tag once a button is pressed. function customizeText() { document.getElementById('centretext').innerHTML = ...

The PHP file fails to load JavaScript because it requires another JavaScript file to be loaded beforehand

I created a chat widget using PHP (chat.php) and now I want to embed it into another PHP page (blank.php). To make this work, chat.php requires some scripts that are located in external JavaScript files. So, in blank.php, I added the necessary JS files th ...

What is the best way to loop through properties of a Typescript interface?

I am currently working with an interface called FilterData, which has the following structure: export interface FilterData { variables?: string[]; processDefinitionKey?: string; } When I make a request to the server, I receive an object named filterS ...

Meteor: Allowing users to share their personal fields on their account

I am currently facing an issue with publishing "friends" from the users Collection. Within each account, there is a field named addressbook that stores all friend-ids. Unfortunately, when trying to publish this information, I only receive my own account (a ...

What is the technique for anchoring elements at the top of the page when scrolling?

There is a common design feature where the sidebar on the left starts at a lower position on the page, but as you scroll it moves up to the top and remains fixed in place instead of disappearing off the screen. This is a popular design trend that I have ...

How can you dynamically change the value of a React datetime slider picker using code?

I can't figure out how to programmatically update the value of the React datetime slider picker, especially when I click on a button. The rendering code for the widget looks like this: <RDSPwidget enableSecond={true} /> This is how my ...

What is the alternative method for creating a unique HTML tag without utilizing customElement?

I'm looking to set up a custom HTML tag using JavaScript specifically for Firefox. Unfortunately, the customElement.define() method is not working for me as it's not supported by default in Firefox. My attempt at using document.registerElement d ...

New updates are not appearing in the Azure app service for a React application when using GitHub actions

I have configured the Azure app service with default settings, and there are no warnings or errors during the build process. The deployment was successful, but the changes do not reflect in the production environment; it still shows the previous build. H ...

The attribute 'y' is not found within the scope of 'DefaultRootState'

In the directory src/reducers/index.tsx, I organize and output all my reducers like so: import counterReducer from '../reducers/counter'; import loggedReducer from '../reducers/isLogged'; import {combineReducers} from 'redux'; ...

How can we consolidate an array of objects containing 6 keys into fewer keys?

Apologies for any confusion caused by the title. As a newcomer to JavaScript, I may not be able to articulate exactly what I am trying to achieve. Hence, I will showcase my code and explain the desired outcome instead. Here is an array of objects I am wor ...

Troubleshooting issue with file upload feature in Angular for Internet Explorer 9

I have implemented a file upload method using the following code: <input type="file" name="upload-file" ng-model= "excelFile" accept=".xlsx" onchange="angular.element(this).scope().fileChanged(this);" ...

AngularJS is throwing a TypeError because it cannot access the '0' property of an undefined value

for ( var i = 0; i < members.length; i++ ) { var value = value[i]; console.log(value); } Feeling really bewildered by how this could be incorrect... 'i' is set to zero, so it's perplexing how the value couldn' ...

MUI Input component does not support the use of the oninput attribute

My MUI Input component is defined like this, but the oninput attribute doesn't seem to work in MUI: <Input variant="standard" size="small" type="number" inputProps={{ min: '0', o ...

Functionality not functioning within Shadow DOM

After creating and exporting an Angular Element as a single script tag (user-poll.js), using the element on a host site is simple. Just include the following two lines: <user-poll></user-poll> <script src="path/to/user-poll.js"></sc ...

Troubleshooting: Missing MapState in Vuex4 for Vue3 within an MVC project

Within my MVC project, I have successfully integrated Vue3 with Vuex4. However, I have encountered an issue specifically related to the mapState function. Upon using the following import statements, an error is triggered: import Vue from 'vue'; ...

React router fails to display the correct component

My route structure looks like this: <Router history={hashHistory}> <Route name="Dashboard" path="/" component={App}> <Route name='Not found' path="*" component={NotFound}/> </Route> </Router> Whi ...

Issue with sound not playing on iOS for video and audio elements (not related to autoplay)

My latest creation is a unique "app" that features a cozy fireplace video and a festive Christmas song playing on repeat. I shared it with a friend who uses an iPhone, but unfortunately, the audio wouldn't play from either the video or the song. I&ap ...

Conditionals in Angular 2 using Sass

Looking to apply this style with Sass or CSS: IF :host.form-control MATCHES .ng-valid[required] OR .ng-valid.required THEN :host ::ng-deep input.form-control = border-left: 5px solid #42A948; Appreciate the help! ...

Tips on transforming two same-length arrays into a single array of objects using JavaScript

I have a dilemma with two arrays that have been structured as follows: arr1 = [10, 20, 30, 40, 50]; arr2 = ['x', 'y', 'z', 'w', 'v']; My goal is to utilize JavaScript in order to transform these arrays of ...