0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Reactの勉強⑤ タブ制御

Posted at

今回の狙い
Web画面の機能といえば、メニューがあり、色々なサブ画面から成り立つ…
ですが、ちょっとした画面機能の場合、タブによる画面切替があると便利ですよね。
結構、曲者なのは、「ちょっとした」画面機能である事。
「ちょっと」なら、1プログラムで作っても、可読性が下がることはない…はず?
実際に構築してみると、あ~らあら… そんなこと多々ですよね。
という事で、今回は、見易いタブ画面のプログラム作成です。
image.png

実装
reactの初期状態を、極力、そのまま残して、簡単に作ってみました。

フォルダ構成
└── src
   ├── App.css   (初期のcssを残してます) 
   ├── App.js    (ほぼ初期のままです) 
   ├── index.css (初期のcssを残してます) 
   ├── index.js  (ほぼ初期のままです) 
   ├── Page1.js  (タブの制御処理です) 
   ├── Tab01.js  (タブの内容1) 
   └── Tab02.js  (タブの内容2)

以下、順にプログラムを掲載してみます。
<index.js>は、解りやすい箇所だけ残した感じです。

index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

indexに呼び出される<app.js>が以下です。
単純にタブを制御する<Page1.js>を組み込んでいるだけですね。

App.js
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Page1 from './Page1';
import { Box } from '@mui/material';
export default function App() {
  return (
    <Router>
      <Box sx={{ display: 'flex' }}>
          <Routes>
            <Route path="/" element={<Page1 />} />
          </Routes>
      </Box>
    </Router>
  );
}

以下はタブの移動を管理する<Page1.js>になります。

Page1.js
import React, { useState } from 'react';
import { Box, Typography, Tabs, Tab } from '@mui/material';
import Tab01 from './Tab01';
import Tab02 from './Tab02';

function a11yProps(index) {
  return {
    id: `simple-tab-${index}`,
    'aria-controls': `simple-tabpanel-${index}`,
  };
}

export default function Page1() {
  const [value, setValue] = useState(0);
  const handleChange = (event, newValue) => {
    setValue(newValue);
  };
  return (
    <Box sx={{ p: 3, maxWidth: 1200, minWidth: 900, mx: 'auto' }}> 
      <Typography variant="h6">タブの制御</Typography>
      <Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
        <Tabs value={value} onChange={handleChange} aria-label="data catalog tabs">
          <Tab label="文字入力例" {...a11yProps(0)} />
          <Tab label="文字列の制御" {...a11yProps(1)} />
        </Tabs>
      </Box>
      <CustomTabPanel value={value} index={0}><Tab01 /></CustomTabPanel>
      <CustomTabPanel value={value} index={1}><Tab02 /></CustomTabPanel>
    </Box>
  );
}

// CustomTabPanelは共通で利用するため、このファイルに残します。
// プログラムが大きくなった場合は、別のファイルに切り出す等
function CustomTabPanel(props) {
  const { children, value, index, ...other } = props;
  return (
    <div role="tabpanel" hidden={value !== index} id={`simple-tabpanel-${index}`}
         aria-labelledby={`simple-tab-${index}`} {...other}>
      {value === index && <Box sx={{ p: 3 }}>{children}</Box>}
    </div>
  );
}

1つ目のタブ「文字入力例」<Tab01.js>のプログラムです。
image.png

Tab01.js
import React, { useState } from 'react';
import { Box, TextField, Button, Typography } from '@mui/material';

export default function Tab01() {
  const [inputText, setInputText] = useState('');
  const [displayedText, setDisplayedText] = useState('');
  const handleInputChange = (event) => {
    setInputText(event.target.value);
  };
  const handleDisplay = () => {
    setDisplayedText(inputText);
  };
  return (
    <Box>
      <Box sx={{ display: 'flex', gap: 2, alignItems: 'center', mb: 4 }}>
        <TextField label="何か入力してください" variant="outlined" value={inputText} onChange={handleInputChange} fullWidth sx={{ maxWidth: 500 }} />
        <Button variant="contained" onClick={handleDisplay} disabled={!inputText.trim()}>表示</Button>
      </Box>
      <Box>
        <Typography variant="h6">入力されたテキスト:</Typography>
        <Typography variant="body1">{displayedText}</Typography>
      </Box>
    </Box>
  );
}

2つ目のタブ「文字列の制御」<Tab02.js>のプログラムです。
image.png

Tab02.js
import React, { useState } from 'react';
import { Box, TextField, Button, Typography, List, ListItem, ListItemText, Divider } from '@mui/material';
import SearchIcon from '@mui/icons-material/Search';

export default function Tab02() {
  const [itemSearchText, setItemSearchText] = useState('');
  const [displayedKeywords, setDisplayedKeywords] = useState([]);
  const [searchPerformed, setSearchPerformed] = useState(false);
  const handleItemSearchChange = (event) => {
    setItemSearchText(event.target.value);
  };
  const handleItemSearch = () => {
    const keywords = itemSearchText.trim().split(/\s+/).filter(Boolean);
    setDisplayedKeywords(keywords);
    setSearchPerformed(true);
  };
  return (
    <Box>
      <Box sx={{ display: 'flex', gap: 2, alignItems: 'center', mb: 4 }}>
        <TextField label="文字列 (スペース区切りで複数指定可)" variant="outlined" value={itemSearchText} onChange={handleItemSearchChange} fullWidth sx={{ maxWidth: 500 }} />
        <Button variant="contained" onClick={handleItemSearch} startIcon={<SearchIcon />} disabled={!itemSearchText.trim()}>実行</Button>
      </Box>
      {searchPerformed && displayedKeywords.length > 0 && (
        <List dense>
          {displayedKeywords.map((keyword, index) => (
            <React.Fragment key={index}>
              <ListItem alignItems="flex-start">
                <ListItemText primary={
                    <Typography component="span" variant="h6" color="text.primary">キーワード {index + 1}</Typography>
                  } secondary={
                    <Typography sx={{ display: 'block' }} component="span" variant="body1" color="text.secondary">{keyword}</Typography>
                  }
                />
              </ListItem>
              {index < displayedKeywords.length - 1 && <Divider component="li" />}
            </React.Fragment>
          ))}
        </List>
      )}
    </Box>
  );
}

今回タブの内容は、それぞれ独立しているので、解りやすいですが、
本来、パーツ化して、パラメータ一杯で訳が分からなくなる… と。
職人さんであれば、かなり広範囲に理解されることでしょう。
きっと、ルール作りやノウハウも御持ちに違いない。
この程度であれば、Aiも簡単に自動生成してくれます。

以上、Aiに依頼する為の豆知識になる事を祈って、御疲れ様でした~。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?