DeltaRage123
DeltaRage123

Reputation: 83

React - Disable Row on Button Click

I have searched everywhere for this, yet every example seems to not match what I want. Once a button has been clicked, for a particular table row, I need to disable that current row, or even better disable the button on the row.

The code I have previously written just disables the buttons for every row. Which would not be correct. For some context, see the screenshot of the app im writing:

enter image description here

When the user clicks Generate Journey for a particular row, I need to disable the 'Generate Journey' button for that particular row, to stop them from doing it again (Which will cause issues server side).

I'm guessing to someone who is experienced in React, this is an easy task, but ive tried different things and each one does not give me the result I want.

So here's my code:

The render function of the screenshot above is as follows:

 render() {
    return (
        <div className="polls-container">
            <div className="dashboard-title" style={{paddingTop: "2%", marginLeft: "-50px"}}>
                <h2>Dashboard</h2>
            </div>
            {
                !this.state.loading && this.state.results.length > 0 ? (
                    <RouteTable buttonDisabled ={this.state.emulateButtonDisabled} results={this.state.results} generateJourney={this.generateJourney} startEmulation={this.getJourneyEmulations}/>
                ) : null
            }
            {
                !this.state.isLoading && this.state.results.length === 0 ? (
                    <div className="no-polls-found">
                        <span>No Active Journey Generations</span>
                    </div>    
                ): null
            }
            {
                this.state.isLoading ? 
                <LoadingIndicator />: null                     
            }
        </div>
    );
}
}  

This basically calls off to the Route Table component, which renders the table seen in the screenshot. Notice how I pass results={this.state.results} generateJourney={this.generateJourney} startEmulation={this.getJourneyEmulations} down as props.

The results prop is just basically the fetched table data. The code for the 'generateJourney' function is as follows (Executed when Generate Journey button is clicked):

 generateJourney = (customerId, startDate, endDate, linkedCustomers, lat, lng) => {
   let confirmGenerateJourney = window.confirm('Are you sure you want to start this Journey Generation?')
    if (confirmGenerateJourney) {
        fetch('http://10.10.52.149:8081/generate-journeys', {
            method: 'POST',
            mode:'cors',
            headers: {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin': '*',
            },
            body: JSON.stringify( {
                customerId: customerId,
                startDate: startDate,
                endDate: endDate,
                serviceIds: linkedCustomers,
                homeLat: lat,
                homeLng: lng
            })
        }).then(response => response.json())
                .catch(err => console.log(err))

        notification.success({
            message: 'Kinesis Fake Telemetry',
            description: "Journey has Started being Generated",
        });

        fetch('http://localhost:8080/api/routeGen/updateCustomerStatus', {
            method: 'PUT',
            mode:'cors',
            headers: {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin': '*',
            },
            body: JSON.stringify( {
                customerId: customerId,
                status: 1
            })
        }).then(response => response.json())
            .catch(err => console.log(err))

        window.location.reload();
    }
}

Nothing fancy, just simply calling off to API's to POST or PUT data. Following this, here is the code for the startEmulation={this.getJourneyEmulations} which is clicked when the Emulate button is clicked. This basically checks that the journey has been generated before we can emulate it. (Waits for a status of finished)

My RouteTable class is then as follows:

 export class RouteTable extends Component {

constructor(props) {
    super(props);
}

getStatus(result) {
    let status;
    if (result.customerStatus === 0) {
        status = (<Badge color={'secondary'}> Pre Journey Generation</Badge>)
    } else if(result.customerStatus === 1) {
        status = (<Badge color={'success'}> In Journey Generation</Badge>)
    } else if (result.customerStatus === 2) {
        status = (<Badge color={'info'}> Ready for Emulation</Badge>)
    } else {
        status = (<Badge href="/journeyEmulation" color={'danger'}> In Emulation</Badge>)
    }
    return status;
}

render() {
    const {startEmulation, buttonDisabled} = this.props;
    const items = this.props.results.map(result => {
        const serviceIdList = [];
        const deviceRequests = [];

        result.linkedCustomers.map(x => { const emulationData = {"imei": x.imei, "serviceId": x.serviceId, "deviceType": "CALAMP"}
            deviceRequests.push(emulationData)
        });

         result.linkedCustomers.map(x => {serviceIdList.push(x.serviceId);});

        return (
            <tr key={result.linkedCustomerId}>
                <th scope="row">{result.customerName}</th>
                <td>{result.linkedCustomerId}</td>
                <td>{result.numDevices}</td>
                <td>{result.startDate}</td>
                <td>{result.endDate}</td>
                <td>{result.lat}</td>
                <td>{result.lng}</td>
                <td> {this.getStatus(result)}</td>

                <td>
                    <div style={{width:"100%"}}>
                        <Button style={{width: "50%"}} color="primary" onClick={() => this.props.generateJourney(result.linkedCustomerId, result.startDate, result.endDate, serviceIdList, result.lat, result.lng)} disabled={buttonDisabled}>Generate Journey</Button>
                        {' '}
                        <Button style={{width: "50%", marginTop: "4%"}} color="danger" onClick={() => startEmulation(result.linkedCustomerId, result.customerName, result.startDate, result.endDate, deviceRequests)}>Emulate Journey</Button>
                    </div>
                </td>
            </tr>
        )
    })

    return (
        <div className="tableDesign" style={{marginTop: "2%"}}>
        <Table hover>
            <thead>
            <tr>
                <th>Customer Name</th>
                <th>Kinesis Customer Id</th>
                <th>Number of Devices</th>
                <th>Start Date</th>
                <th>End Date</th>
                <th>Home Lat</th>
                <th>Home Long</th>
                <th>Status</th>
                <th>Actions</th>
            </tr>
            </thead>
            <tbody>
            {items}
            </tbody>
        </Table>
        </div>
    )
}

Now my problem is how do I disable the user from generating the journey twice? I have tried the solution posted which just disables all buttons for every row. Any help would be massively appreciated as this is becoming frustrating!! Im guessing I can somehow use the row key of the table <tr key {result.linkedCustomerId}> to target the specific button to disable?

Thanks for your help :)

***** EDIT *****

 generateJourney = (customerId, startDate, endDate, linkedCustomers, lat, lng) => {
   let confirmGenerateJourney = window.confirm('Are you sure you want to start this Journey Generation?')
    if (confirmGenerateJourney) {

        this.setState({generatingId: customerId});

        fetch('http://10.10.52.149:8080/generate-journeys', {
            method: 'POST',
            mode:'cors',
            headers: {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin': '*',
            },
            body: JSON.stringify( {
                customerId: customerId,
                startDate: startDate,
                endDate: endDate,
                serviceIds: linkedCustomers,
                homeLat: lat,
                homeLng: lng
            })
        }).then(response => {
            this.setState({generatingId: null});
            // response.json();
        }).catch(err => {
            this.setState({generatingId: null});
            console.log(err)
        })

        notification.success({
            message: 'Kinesis Fake Telemetry',
            description: "Journey has Started being Generated",
        });

        fetch('http://localhost:8080/api/routeGen/updateCustomerStatus', {
            method: 'PUT',
            mode:'cors',
            headers: {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin': '*',
            },
            body: JSON.stringify( {
                customerId: customerId,
                status: 1
            })
        }).then(response => response.json())
            .catch(err => console.log(err))

        // window.location.reload();
    }
}

Props passed down to RouteTable

 {
                !this.state.loading && this.state.results.length > 0 ? (
                    <RouteTable generatingId ={this.state.generatingId} results={this.state.results} generateJourney={this.generateJourney} startEmulation={this.getJourneyEmulations}/>
                ) : null
            }

Then in RouteTable:

                            <Button style={{width: "50%"}} color="primary" onClick={() => this.props.generateJourney(result.linkedCustomerId, result.startDate, result.endDate, serviceIdList, result.lat, result.lng)} disabled={this.props.generatingId === result.linkedCustomerId}>Generate Journey</Button>

Upvotes: 0

Views: 4348

Answers (1)

Edrian
Edrian

Reputation: 608

One way to do it is:

  1. On your component that uses RouteTable, make a new state called generatingId.
  2. Make a new prop for the <RouteTable> called generatingId and make this.state.generatingId as its value.
  3. On your generateJourney function, do a this.setState({generatingId: customerId}) before the AJAX call. Then inside the .then and .catch, make a this.setState({generatingId: null})
  4. Now inside your RouteTable component, update your Row's generate journey button to be like so:
<Button style={{width: "50%"}} color="primary" onClick={() => this.props.generateJourney(result.linkedCustomerId, result.startDate, result.endDate, serviceIdList, result.lat, result.lng)} disabled={this.props.generatingId === result.linkedCustomerId}>Generate Journey</Button>

What would happen is when you click and the Generate Journey button, the generateJourney function will set the customer's ID as the ID that is being generated. This new value will get passed to your RouteTable and will then render your table, and since the button of the rows will check if the generatingId prop is equal to the customerId the row is for, it will disable the button on that row if the statement is true.

Upvotes: 1

Related Questions