複数のアイテムがあり、それぞれのアイテムは非選択/選択状態を持つことができる、そういうUIを HTMLとReactを使ってつくります。
Node.js は使わないで、index.html に直接 JavaScript を書いていく方法でつくります。
まず出発点となる 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で表示するところまで記述しました。
次に、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>
);
}
ブラウザで表示させると次のようになります。
それでは、いよいよ振る舞いを追加します。 ボタンをクリックしたら、選択状態に変更します。
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 をリロードすれば:
このように、クリックに応じて選択/非選択状態を切り替える機能が実装できました。
最後に、完成した 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>