Home About Contact
React

React で複数選択ボタンをつくる

複数のアイテムがあり、それぞれのアイテムは非選択/選択状態を持つことができる、そういうUIを HTMLとReactを使ってつくります。

Node.js は使わないで、index.html に直接 JavaScript を書いていく方法でつくります。

step3

まず出発点となる index.html:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>multiple select buttons</title>
        <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
        <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
        <!-- Don't use this in production: -->
        <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    </head>
    <body style="margin: 20px;">
        <div id="app"></div>
    
        <script type="text/babel">
            class App extends React.Component {
                constructor(props) {
                    super(props);
    
                    const createTag = (name)=>{
                        return {
                            name: name,
                            selected: false};
                    };
    
                    const tagList = [
                        createTag('kotlin'),
                        createTag('python'),
                        createTag('groovy'),
                        createTag('javascript'),
                        createTag('haskell')];
    
                    this.state = {
                        tagList: tagList
                    };
                }
    
                render(){
                    return (
                        <ul>
                            {this.state.tagList.map((tag)=>{
                                return <li><button>{tag.name}</button></li>
                            })}
                        </ul>
                    );
                }
            }
    
            const container = document.getElementById('app');
            const root = ReactDOM.createRoot(container);
            root.render(<App />);
        </script>
    </body>
</html>

kotlin, python, groovy.. などのタグ情報を列挙しています。 これらのタグ情報をボタンとしてHTMLで表示するところまで記述しました。

step1

次に、render() 関数を修正します。 スタイルを適宜追加して見た目を修正しました。

render(){
    const liStyle = {
        display: 'inline',
        margin: '5px'
    };

    const buttonStyle = {
        backgroundColor: '#eee',
        borderRadius: '2px',
        border: 'solid 1px'
    };
    const buttonSelectedStyle = {
        backgroundColor: '#ccf',
        borderRadius: '2px',
        border: 'solid 1px'
    };

    return (
        <ul>
            {this.state.tagList.map((tag)=>{
                return <li style={liStyle}><button style={buttonStyle}>{tag.name}</button></li>
            })}
        </ul>
    );
}

ブラウザで表示させると次のようになります。

step2

それでは、いよいよ振る舞いを追加します。 ボタンをクリックしたら、選択状態に変更します。

li 要素をレンダリングしていたこの部分を:

return <li style={liStyle}><button style={buttonStyle}>{tag.name}</button></li>                

次のように変更します。

return (
    <li style={liStyle}>
        <button onClick={()=>buttonClicked(tag)} style={buttonStyle}>{tag.name}</button>
    </li>);

onClick={()=>buttonClicked(tag)} を追加して、このボタンがクリックされたときに buttonClicked() 関数を呼び出すようにしました。

当然 buttonClick 関数を用意する必要があります:

const buttonClicked = (tag)=> {
    console.log(tag.name);
};

まずは、クリックがあったらそのタグ名をコンソールに出力するだけにしておきます。

これで index.html をブラウザでリロードしてタグボタンをクリックして、コンソールに、クリックしたタグ名が出力されるか確認しましょう。

問題がなければ、今度は state の更新を行うコードをこの関数に追加します。

const buttonClicked = (tag)=> {
    const updatedTagList = this.state.tagList.map((it)=>{
        if(it.name == tag.name){
            return {name: it.name, selected: !it.selected};
        } else {
            return it;
        }
    });

    this.setState({
        tagList: updatedTagList
    });
};

タグ名をキーに該当のタグの selected を 反転させています。 つまり、非選択状態の場合は選択状態に、選択状態は非選択状態に変更します。 そうやって新しい tagList の状態をつくりだして、それを this.setState() することで表示の更新を促します。 (状態を変更するとあとは React が表示を更新してくれます。)

あとは、この状態に応じてボタンの表示(スタイル)を切り替えます。

return (
    <li style={liStyle}>
        <button
            onClick={()=>buttonClicked(tag)}
            style={tag.selected ? buttonSelectedStyle : buttonStyle}>
            {tag.name}
        </button>
    </li>);

三項演算子で、tag.selected が true なら buttonSelectedStyle を、そうでなければ、buttonStyle を適用しています。

これで index.html をリロードすれば:

step3

このように、クリックに応じて選択/非選択状態を切り替える機能が実装できました。

まとめ

最後に、完成した index.html 全体を掲載します。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>multiple select buttons</title>
    
        <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
        <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
        <!-- Don't use this in production: -->
        <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    </head>
    <body style="margin: 20px;">
        <div id="app"></div>
    
        <script type="text/babel">
            class App extends React.Component {
                constructor(props) {
                    super(props);
    
                    const createTag = (name)=>{
                        return {
                            name: name,
                            selected: false};
                    };
    
                    const tagList = [
                        createTag('kotlin'),
                        createTag('python'),
                        createTag('groovy'),
                        createTag('javascript'),
                        createTag('haskell')];
    
                    this.state = {
                        tagList: tagList
                    };
                }

    
                render(){
                    const buttonClicked = (tag)=> {
                        //console.log(tag.name);
                        const updatedTagList = this.state.tagList.map((it)=>{
                            if(it.name == tag.name){
                                return {name: it.name, selected: !it.selected};
                            } else {
                                return it;
                            }
                        });

                        this.setState({
                            tagList: updatedTagList
                        });
                    };

                    const liStyle = {
                        display: 'inline',
                        margin: '5px'
                    };

                    const buttonStyle = {
                        backgroundColor: '#eee',
                        borderRadius: '2px',
                        border: 'solid 1px'
                    };
                    const buttonSelectedStyle = {
                        backgroundColor: '#ccf',
                        borderRadius: '2px',
                        border: 'solid 1px'
                    };

                    return (
                        <ul>
                            {this.state.tagList.map((tag)=>{
                                return (
                                    <li style={liStyle}>
                                        <button
                                            onClick={()=>buttonClicked(tag)}
                                            style={tag.selected ? buttonSelectedStyle : buttonStyle}>
                                            {tag.name}
                                        </button>
                                    </li>);
                            })}
                        </ul>
                    );
                }
            }
    
            const container = document.getElementById('app');
            const root = ReactDOM.createRoot(container);
            root.render(<App />);
        </script>
    </body>
</html>