1

I have multiple buttons on a page, when user click on each button it should show a loading, I do this with a class, but there is a problem, it add loading to all button but I just need to add class to this button (which is clicked not all!)

You can see a simple demo Here

handleClick = (e) => {
    this.setState({
  loading: 'loading'
  })
}

I am aware that all buttons use the same state but how can I use changed state on clicked button not all. what is the best practice? should I use ref ? or should I use multiple states? but I don't know how many buttons will be add on this page in the future. any idea to give a solution or trick.

9
  • This can be one of the option jsfiddle.net/cm28rdo6 Commented Dec 31, 2019 at 6:47
  • @ankitkanojia yes, good but it won't work if you click on all buttons, for example click on the first button, then second, it just show loading on a single button Commented Dec 31, 2019 at 6:50
  • 1
    Because when it show loading, it post something on api and when it give success, loading should remove, but this happen on other function, not handleClick, so how can I select that button for remove loading class? @ankitkanojia Commented Dec 31, 2019 at 7:07
  • 1
    try this jsfiddle.net/wLp83rec in case of you want to remove loading on API response then you need to simply call a function this.removeLoading('btn1') | this.removeLoading('btn2') | this.removeLoading('btn3') accordingly Commented Dec 31, 2019 at 7:16
  • 1
    please review this example, jsfiddle.net/61fpb524/1 the dynamic example with API as well. verify it and in case of still required any chnages please do let me know Commented Dec 31, 2019 at 7:31

4 Answers 4

4

Here is the dynamic button binding with handled click event separately, Even added Axios API call so that in API response you can remove loading on that button as well.

Here is a working demo

const {
    Button
} = semanticUIReact


class App extends React.Component {

    handleClick = (name) => {
        this.refs[name].ref.classList.add('loading');
        axios.get('https://simonbreiter.com/wp-json/wp/v2/projects?_embed')
            .then(response => {
                setTimeout(() => {
                    this.removeLoading(name)
                }, 3000)
            })
            .catch(error => console.log(error));
    }

    removeLoading = (name) => {
        this.refs[name].ref.classList.remove('loading');
    }

    render() {
        return (
            <div>
                {[...Array(3)].map((data, index) => {
                    const btnIndex = index + 1;
                    return <Button ref={"btn" + btnIndex} className={'btn'} onClick={() => this.handleClick("btn" + btnIndex)}>Button {btnIndex}</Button>
                })}
            </div>
        )
    }
}

ReactDOM.render(<App />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui-react/0.81.2/semantic-ui-react.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.2/semantic.min.css" />
<script src="https://unpkg.com/[email protected]/dist/axios.min.js"></script>

<div id="app"></div>

Sign up to request clarification or add additional context in comments.

2 Comments

What about React.createRef()? I think OP didn't try your code at all, just feel this right!
@Pedram React.createRef() is the bind method that can be defined in the constructor but with this example, we just required to toggle class so we can use them directly rather than initialize reference. We can follow either option. Both are equivalent.
3

You can easily get the selected button using your handleClick method. And then toggle the loading class. Here is a working demo

const {
  Button
} = semanticUIReact


class App extends React.Component {


handleClick = (e) => {
  // You can use .add or .toggle according to your requirement.
  e.target.classList.toggle('loading');
}

render() {
  return (
    <div>
       <Button className='btn' onClick={this.handleClick}>Button 1</Button>
       <Button className='btn' onClick={this.handleClick}>Button 2</Button>
       <Button className='btn' onClick={this.handleClick}>Button 3</Button>
    </div>
  )
}
}

ReactDOM.render(<App />, document.getElementById('app'));

1 Comment

Oh, i get it now. You can easily add a remove class method when you receive a response from server
1

As per my understanding to this question, the better approach is to handle/maintain state of such generic component to it's own component level. I have prepared one example on the basis of the code provided by you. https://jsfiddle.net/y0cjbodq/

const {
    Button
} = semanticUIReact

class ButtonComp extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            loading: false
        }
        this.handleClick = this.handleClick.bind(this);
    }


    handleClick(e) {
        this.setState({
            loading: !this.state.loading
        });
    }

    render() {
        return (
            <Button className={'btn ' + (this.state.loading ? 'loading' : null)} onClick={this.handleClick}>Button 1</Button>
        )
    }
}

class App extends React.Component {
    // Moved state management to component level
    render() {
        return (
            <div>
                <ButtonComp />
                <ButtonComp />
                <ButtonComp />
            </div>
        )
    }
}

ReactDOM.render(<App />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui-react/0.81.2/semantic-ui-react.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.2/semantic.min.css"/>

<div id="app"></app>

Comments

1

You can create an array of buttons. Then you can compare selected index with iteration index and can apply loading class.

const {
    Button
} = semanticUIReact

class App extends React.Component {

    state = {
        clickedBtnIndex: -1,
    }

    buttons = ["Button 1", "Button 2", "Button 3"];

    handleClick = (i) => {
        this.setState({
            clickedBtnIndex: i
        })
    }

    render() {
        return (
            <div>
                {
                    this.buttons.map((btn, i) =>
                        <Button className={'btn ' + (i === this.state.clickedBtnIndex ? "loading" : "")} onClick={() => this.handleClick(i)}>
                            {btn}
                        </Button>
                    )
                }
            </div>
        )
    }
}

ReactDOM.render(<App />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui-react/0.81.2/semantic-ui-react.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.3.2/semantic.min.css" />

<div id="app"></div>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.