TODOアプリの作成
1つのコンポーネントで実装
create-react-app todo
cd todo;
npm install react-router-dom
- App.jsを修正
import React from 'react';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
todos: [],
newTodo: '',
};
}
handleChange = (e) => {
this.setState({newTodo: e.target.value});
}
addTodo = () => {
if (this.state.newTodo === ''){
return;
}
const todos = this.state.todos;
todos.push(this.state.newTodo);
this.setState({newTodo: ''});
this.setState({todos: todos});
}
deleteTodo = (e) => {
const todos = this.state.todos;
todos.splice(e.target.dataset.index, 1);
this.setState({todos: todos});
}
render() {
return (
<div>
<header>
<h1>Todoアプリ</h1>
</header>
<main>
<input value={this.state.newTodo} onChange={this.handleChange}/>
<button onClick={this.addTodo}>追加</button>
<h2>Todoリスト</h2>
<ul>
{
this.state.todos.map((todo, i) => {
return (
<li key={i}>{todo}<button onClick={this.deleteTodo} data-index={i}>削除</button></li>
);
})
}
</ul>
</main>
</div>
);
}
}
複数のコンポーネントで実装
- TodoForm.jsを作成
import React from "react";
export default class TodoForm extends React.Component {
constructor(props) {
super(props);
this.state = {
newTodo: '',
};
}
handleChange = (e) => {
this.setState({newTodo: e.target.value});
}
addTodo = () => {
if (this.state.newTodo === ''){
return;
}
this.props.addTodo(this.state.newTodo);
this.setState({newTodo: ''});
}
render() {
return (
<React.Fragment>
<input value={this.state.newTodo} onChange={this.handleChange}/>
<button onClick={this.addTodo}>追加</button>
</React.Fragment>
)
}
}
- TodoList.jsを作成
import React from "react";
export default class TodoList extends React.Component {
deleteTodo = (e) => {
this.props.deleteTodo(Number(e.target.dataset.index));
}
render() {
return (
<React.Fragment>
<h2>Todoリスト</h2>
<ul>
{
this.props.todos.map((todo, i) => {
return (
<li key={i}>{todo}<button onClick={this.deleteTodo} data-index={i}>削除</button></li>
);
})
}
</ul>
</React.Fragment>
)
}
}
- App.jsを修正
TodoFormには追加用メソッド、TodoListには削除用メソッドと配列todos、をpropsで渡す。
import React from 'react';
import TodoForm from './components/TodoForm';
import TodoList from './components/TodoList';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
todos: [],
};
}
addTodo = (todo) => {
const todos = this.state.todos;
todos.push(todo);
this.setState({todos: todos});
}
deleteTodo = (index) => {
const todos = this.state.todos;
todos.splice(index, 1);
this.setState({todos: todos});
}
render() {
return (
<div>
<header>
<h1>Todoアプリ</h1>
</header>
<main>
<TodoForm addTodo={this.addTodo}/>
<TodoList deleteTodo={this.deleteTodo} todos={this.state.todos}/>
</main>
</div>
);
}
}
ローカルストレージの利用
- App.jsを修正
componentDidMount() {
const todos = JSON.parse(localStorage.getItem('todos')) || [];
this.setState({todos: todos});
}
// addTodoメソッド、deleteTodoメソッドに追加localStorage.setItem('todos', JSON.stringify(this.state.todos));
コンポーネントの独立
「App.jsでデータを保持し、propsでコンポーネントに渡す」から「App.jsではデータを保持せず、コンポーネントから直接ローカルストレージにアクセス」に変更。
※追加ボタンを押してもリロードしないとリストは更新されない。
- App.jsを修正
※App.jsはコンポーネントを呼び出すだけ<TodoForm2/>
<TodoList2/>
import React from 'react';
import TodoForm2 from './components/TodoForm2';
import TodoList2 from './components/TodoList2';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
todos: [],
};
}
render() {
return (
<div>
<header>
<h1>Todoアプリ</h1>
</header>
<main>
<h2>コンポーネントの独立</h2>
<TodoForm2/>
<TodoList2/>
</main>
</div>
);
}
}
- TodoForm2.jsを作成
addTodoメソッドでローカルストレージの取得・保存。const todos = JSON.parse(localStorage.getItem('todos2')) || [];
およびlocalStorage.setItem('todos2', JSON.stringify(todos));
import React from "react";
export default class TodoForm2 extends React.Component {
constructor(props) {
super(props);
this.state = {
newTodo: '',
};
}
handleChange = (e) => {
this.setState({newTodo: e.target.value});
}
addTodo = () => {
if (this.state.newTodo === ''){
return;
}
const todos = JSON.parse(localStorage.getItem('todos2')) || [];
todos.push(this.state.newTodo);
localStorage.setItem('todos2', JSON.stringify(todos));
this.setState({newTodo: ''});
}
render() {
return (
<React.Fragment>
<input value={this.state.newTodo} onChange={this.handleChange}/>
<button onClick={this.addTodo}>追加</button>
</React.Fragment>
)
}
}
- TodoList2.jsを作成
deleteTodoメソッドでローカルストレージに保存する。localStorage.setItem('todos2', JSON.stringify(todos));
componentDidMountメソッドでローカルストレージの取得。componentDidMount() {
const todos = JSON.parse(localStorage.getItem('todos2')) || [];
this.setState({todos: todos});
}
import React from "react";
export default class TodoList2 extends React.Component {
constructor(props) {
super(props);
this.state = {
todos: [],
};
}
deleteTodo = (e) => {
const todos = this.state.todos;
todos.splice(Number(e.target.dataset.index), 1);
this.setState({todos: todos});
localStorage.setItem('todos2', JSON.stringify(todos));
}
componentDidMount() {
const todos = JSON.parse(localStorage.getItem('todos2')) || [];
this.setState({todos: todos});
}
render() {
return (
<React.Fragment>
<h2>Todoリスト</h2>
<ul>
{
this.state.todos.map((todo, i) => {
return (
<li key={i}>{todo}<button onClick={this.deleteTodo} data-index={i}>削除</button></li>
);
})
}
</ul>
</React.Fragment>
)
}
}
ルーティングの利用
- App.jsを修正
import { Route } from "react-router-dom";
を追加し、<Route path="/add" component={TodoForm2} exact></Route>
<Route path="/" component={TodoList2} exact></Route>
とする。
- index.jsを修正
import { BrowserRouter } from "react-router-dom";
を追加し、<BrowserRouter>
<App />
</BrowserRouter>
とする。
- TodoForm2.jsを修正
import { Link } from "react-router-dom";
を追加し、<Link to="/">一覧へ</Link>
とする。
- TodoList2.jsを修正
import { Link } from "react-router-dom";
を追加し、<Link to="/add">登録フォームへ</Link>
とする。
Material-UIの利用
コンポーネントがたくさんある。
- 公式サイト:https://material-ui.com/getting-started/usage/
- テンプレート:https://github.com/mui-org/material-ui/tree/master/docs/src/pages/getting-started/templates
- インストール
npm install @material-ui/core
npm install @material-ui/icons
- App.jsに追記
Buttonを使う場合import Button from '@material-ui/core/Button';
を追加し、<Button variant="contained" color="primary">Hello World</Button>
とする。
いろいろなコンポーネントを使ったサンプル
import React from 'react';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Typography from '@material-ui/core/Typography';
import ArrowBack from '@material-ui/icons/ArrowBack';
import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import CardActions from '@material-ui/core/CardActions';
import TextField from '@material-ui/core/TextField';
import Button from '@material-ui/core/Button';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import IconButton from '@material-ui/core/IconButton';
import AddIcon from '@material-ui/icons/Add';
import DeleteIcon from '@material-ui/icons/Delete';
import Divider from '@material-ui/core/Divider';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
newTodo: ''
}
}
render() {
return (
<div>
<AppBar position="static">
<Toolbar>
<ArrowBack style={{color: 'white'}}/>
<Typography variant="title" color="inherit" style={{width: '90%', textAlign: 'center'}}>
My Todo
</Typography>
</Toolbar>
</AppBar>
<Card>
<CardContent>
<TextField> value={this.state.newTodo}></TextField>
</CardContent>
<CardActions>
<Button variant="contained" color="primary">追加</Button>
<Button variant="outlined" color="primary" aria-label="Add"><AddIcon/></Button>
</CardActions>
</Card>
<list>
{
['111', '222', '333'].map((str) => {
return (
<List>
<ListItem key={str}>
<ListItemText>{str}</ListItemText>
<IconButton variant="fab" ariia-label="delete">
<DeleteIcon/>
</IconButton>
</ListItem>
<Divider/>
</List>
)
})
}
</list>
</div>
);
}
}
Webサーバ上での実行
npm run build
で生成されるbuildディレクトリをサーバへアップする。
※ ルーティングがHistoryモードの場合、/addなどを表示してからブラウザをリロードすると404エラーとなってしまう。
Rewriteで全リクエストをindex.htmlに書き換える必要がある。
https://qiita.com/yakipudding/items/78e68ef31e55f559c0dc