How do I get my dropdown in react to update without having to refresh?

Viewed 1863

I have a dropdown in react that I am able to add and delete options to perfectly fine, however, my problem is that the dropdown only updates after I refresh the page. I want it to reflect the new addition or deletion automatically after pressing either the add or delete buttons. I believe the answer has to do with updating the state after the onSubmit and deleteRole methods have been called. I am fairly new to react and would appreciate any guidance. Thanks in advance. Also here is my code and here is a link to what the dropdown looks like: http://a85febb5.ngrok.io/roles

import React, { Component } from 'react';
import Modal from 'react-bootstrap/Modal';
import Button from 'react-bootstrap/Button';
import InputGroup from 'react-bootstrap/InputGroup';
import FormControl from 'react-bootstrap/FormControl'
import axios from 'axios';
import Select from 'react-select';

// modal docs: https://react-bootstrap.github.io/components/modal/

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

      this.updateInput = this.updateInput.bind(this);
      this.onSubmit = this.onSubmit.bind(this);
      this.deleteRole = this.deleteRole.bind(this)

      this.state = { 
        show: false,
        role: '',
        roles: [],
        selectedOption: null,
        id: null,
      }
    }

    // opens and closes the modal on click of Edit Roles Button

    handleClose = () => {
      this.setState({ show: false})
    }

    handleShow = () => {
      this.setState({ show: true})
    }

    // gets roles data from database

    componentDidMount() {
      axios.get('http://localhost:4000/roles')
          .then(res => {
              this.setState({ roles: res.data });
          })
          .catch(function (error) {
              console.log(error);
          })
    }

    // When a role is selected in the dropdown the id of that role is set in the state

    handleChage = (selectedOption) => {
      this.setState({ selectedOption });
      this.setState({ id: selectedOption.id})
    }

    // creates an array of roles

    rolesList() {
      return this.state.roles.map(currentrole => ({
        label: currentrole.role_title, value: currentrole.role_title, id: currentrole._id
      }))
    }

    updateInput = (e) => {
      this.setState({
        role: e.target.value
      })
    }

    Capitalize(str){
      return str.charAt(0).toUpperCase() + str.slice(1);
    }

    onSubmit() {

      const roleadd = {
        role_title: this.Capitalize(this.state.role)
      }

      console.log(roleadd);

      axios.post('http://localhost:4000/roles/add', roleadd)
        .then(res => console.log(res.data));

      this.setState({
        role: '',
      })
    }

    deleteRole(id) {

      // remove deleted item from state

      axios.delete('http://localhost:4000/roles/'+this.state.id)
        .then(response => { console.log(response.data)})

      this.setState({
        roles: this.state.roles.filter(el => el.id !== id)
      })

    }

    render() { 

    console.log(this.state.id)  
    const roles = this.rolesList()
    console.log(roles)
    console.log(this.state.roles)

    return (
    <>
      <Button style={{marginLeft: '20px'}} variant="outline-success" onClick={this.handleShow}>
        Edit Roles
      </Button>

      <Modal show={this.state.show} onHide={this.handleClose} animation={true}>
        <Modal.Header closeButton>
          <Modal.Title>Edit Roles</Modal.Title>
        </Modal.Header>
        <Modal.Body style={{height: '300px'}}>

          <div style={{display: 'flex',justifyContent: 'center', marginBottom: '20px' }}>
              <InputGroup className="mb-3">
                  <FormControl 
                  role={this.state.role} 
                  value={this.state.role} 
                  onChange={this.updateInput} 
                  style={{width: '400px'}}
                  placeholder="Add new role"
                  aria-label="Add new role"
                  aria-describedby="basic-addon2"
                  />
                  <Button onClick={this.onSubmit} style={{marginLeft: '10px', width: '75px'}} variant="success">Add</Button>
              </InputGroup>
          </div>

          <div style={{display: 'flex',justifyContent: 'center'}}>
          <div style={{width: '400px'}}>
            <Select onChange={this.handleChage} options={ roles } />
          </div>
          <Button onClick={this.deleteRole} style={{marginLeft: '10px'}} variant="danger">Delete</Button>
          </div>

        </Modal.Body>
        <Modal.Footer>
          <Button variant="outline-secondary" onClick={this.handleClose}>
            Close
          </Button>
          <Button variant="outline-primary" onClick={this.handleClose}>
            Save Changes
          </Button>
        </Modal.Footer>
      </Modal>
    </>
    );
  }
}

export default Rolesmodal;
2 Answers
onSubmit() {

      const roleadd = {
        role_title: this.Capitalize(this.state.role)
      }

      console.log(roleadd);

      axios.post('http://localhost:4000/roles/add', roleadd)
        .then(res => console.log(res.data));

      this.setState({
        role: '',
        roles: [...roles, roleadd]

      })

Also update the roles array to reflect the added role

The dropdown component get the value, but not rendering it. Since it will shallow comparing the old data prop with new one which is object reference thus it will think that nothing has changed and won't re-render the child component; in order to solve that you need to force react to re-render your component on every parent component render; React will remount the component if its key change; a simple solution is to add key to your select component (Ideally based on roles therfore only changes when a role is changed) but if you can't figure out a perfect key value, you can use a new key on every render based on a random value, like:

<Select key={Math.floor(Math.random() * 99999) onChange={this.handleChage} options={ roles } />

this key will definitely change on every render of parent but my not be most efficient way; however for simple use-case like that is fine;

Related