ssuhat
ssuhat

Reputation: 7656

React - Set loading state only on specific clicked button

Here is my current code

class cart extends Component {
    state = { loading: [] };

    addToCart = (e, id) => {
        let loading = this.state.loading.slice();
        loading[e] = true;
        this.setState({
            loading,
        })
    };
    render() {
        const { data } = this.props;

        return (
            <div>
            {data.map(catering => {
                const { menus } = catering;

                return (
                    <Row>
                    {menus.map(menu => (
                        <Col xs={12} md={12} lg={6} key={menu.id} className="m-bottom-15">
                        <Card style={{ height: '165px', border: 0, overflow: 'hidden' }}>


                        <CardActions>
                        <LoadingButton
                        className="button small primary pull-right"
                        loading={thi.state.loading || false}
                        onClick={e => this.addToCart(e, menu.id)}
                        >
                        <MdAdd size={18} color={white} />
                        <span className="font-14 font-400">Add to cart</span>
                        </LoadingButton>

                        </CardActions>
                        </Card>
                        </Col>
                        ))}
                    </Row>

                    );
            }
        }

There will be around 20 button when the map function is done.

What I want to achieve is: every time users click add to cart button, I will call the ajax to save the cart and show loading for the specific clicked button. After ajax is done, return the state back to normal.

On my current code I haven't put my ajax call yet, I still want to make sure the loading button work on press. Right now its not working.

How can I achieve this?

Thanks.

Upvotes: 2

Views: 7191

Answers (3)

Goodlife
Goodlife

Reputation: 3922

for the hooks guys this is what I did

const [isLoading, setIsLoading] = useState([]);
<button
        type="button"
        disabled={isLoading[item.username]}
        className="btn btn-success btn-rounded btn-sm my-0"
        onClick={()=>activateOrDeactivate( item.activated,item.username)}
      >      {isLoading[item.username] && <i className="fa fa-circle-o-notch fa-spin"></i>}
      {isLoading[item.username]&& <span> Activating...</span>}
      {!isLoading[item.username]&& <span>Activate</span>}</button>


 const activateOrDeactivate = (valTrueOrFalse,newusername) => {
    let loading = isLoading.slice();
    loading[newusername] = true;
    setIsLoading(loading)
    let config = {
      headers: {
        Authorization: "Bearer " + state.token
      },
    };

    api.post(
      "process-activation",
      {
        activated: !valTrueOrFalse,
        username:newusername
      },
      config
    ).then(
      response => {
        getAllUsers()
        let loading = isLoading.slice();
        loading[newusername] = false;
        setIsLoading(loading)

Upvotes: 0

Mayank Shukla
Mayank Shukla

Reputation: 104459

Instead of maintaining bool in loading array, maintain the menu ids, whenever Add button is clicked, push the id of that item into loading array. When generating the card check whether loading array have that id or not, if yes then show loading otherwise show the card.

Write it like this:

class cart extends Component {

    state = { loading:  []};

    addToCart = (e, id) => {
        let loading = this.state.loading.slice();
        loading.push(id);
        this.setState({
            loading
        })
    };

    render() {
        const { data } = this.props;

        return (
            <div>
            {data.map((catering,i) => {
                const { menus } = catering;
                return (
                    <Row>
                        {menus.map(menu => {
                            return this.state.loading.indexOf(menu.id) >= 0 ?
                                <Col xs={12} md={12} lg={6} key={menu.id} className="m-bottom-15">
                                    <Card style={{ height: '165px', border: 0, overflow: 'hidden' }}>
                                        <CardActions>
                                            <LoadingButton
                                                className="button small primary pull-right"
                                                loading={thi.state.loading || false}
                                                onClick={e => this.addToCart(e, menu.id)}
                                            >
                                                <MdAdd size={18} color={white} />
                                                <span className="font-14 font-400">Add to cart</span>
                                            </LoadingButton>
                                        </CardActions>
                                    </Card>
                                </Col>
                            :
                            /*Loading icon*/
                        })}
                    </Row>
                )
            })}
        )
    }
}

Upvotes: 0

sam
sam

Reputation: 2820

There is some error in your addToCart method. You should use id as index of loading and set loading array to state as this:

addToCart = (e, id) => {
    let loading = this.state.loading.slice();
    loading[id] = true;
    this.setState({
        loading: loading
    });
};

Also, in your render method, change this.state.loading to this.state.loading[menu.id]:

<LoadingButton
    className="button small primary pull-right"
    loading={this.state.loading[menu.id] || false}
    onClick={e => this.addToCart(e, menu.id)}
>

When ajax call is done, you just call setState function which sets loading array values to false in callback method.

Upvotes: 5

Related Questions