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?

More than 5 years have passed since last update.

React 編 - 初心者こそ、Docker を使おう!

Last updated at Posted at 2019-10-25

React 編

同じ Docker の環境を使って、React を使おうかと思います。
今回も、Pacel-bundler をそのまま使います。
React にしても特に Parcel の設定はいらないです。
では、ライブラリを読み込んで始めましょう

Docker コンテナを起動して、uname で linux が出るようにしてください。
このあたりは、最初の投稿を参照ねがいます。
docker-compose run --service-ports node js が書いているあたりです。

一応 yml ファイルを載せておきます

dcoker-compose.yml

version: "3"
services:
  node:
    container_name: node
    image: atoris1192/node:0.1.5
    # build: .
    # volumes は上書きに注意
    volumes:
      - .:/app
    ports:
      - "1234:1234"
      - "1235:1235"
    working_dir: /app
    command: cp -rp /tmp/src /app
    # command: npx parcel --hmr-port 1235 --hmr-hostname localhost index.pug
    tty: true
docker-compose up  # 同じディレクトリでするなら必要ないです
docker-compose ps
docker-compose run --service-ports node bash
# コンテナ linux
uname
cd src
yarn add react react-dom
npx parcel --hmr-port 1235 --hmr-hostname localhost index.pug

index.pug

<!DOCTYPE html>
html(lang="en")
  head
    meta(charset="UTF-8")
    meta(name="viewport", content="width=, initial-scale=1.0")
    meta(http-equiv="X-UA-Compatible", content="ie=edge")
    title Document
  body
    #app
    script(src="./index.tsx")

index.tsx

import * as React from 'react'
import * as ReactDOM from 'react-dom';
import App from './App';
import './style.stylus'

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

App.tsx

import * as React from 'react';

const todos = [
  {id: 0, title: "Task 0", isDone: false },
  {id: 1, title: "Task 1", isDone: true },
  {id: 2, title: "Task 2", isDone: false },
]


class App extends React.Component {
  private state: any;
  constructor() {
    super();
    // 仮データを直接いれてますが、Reactでは禁止行為です
    this.state = {
      todos: todos,
    }
  }
  render() {
    const todos = this.state.todos.map ( todo => {
      return (<li key={ todo.id }>{ todo.title }</li>)
    })
    return(
      <div className="container">
        <ul>
          { todos }
        </ul>
      </div>
    )
  }
}

export default App;

addTodo

input 処理が面倒くさい。

Att.tsx

import * as React from 'react';

const todos = [
  {id: 0, title: "Task 0", isDone: false },
  {id: 1, title: "Task 1", isDone: true },
  {id: 2, title: "Task 2", isDone: false },
]

function TodoForm(props) {
  return(
    <form onSubmit={ props.addTodo }>
      <input type="text"
        value={ props.item }
        onChange={ props.updateItem }
      />
      <input type="submit" value="Add"/>
    </form>
  )

}

class App extends React.Component {
  private state: any;
  private setState: any;
  constructor() {
    super();
    this.state = {
      todos: todos,
      item: '',
    }
    this.addTodo = this.addTodo.bind(this);
    this.updateItem = this.updateItem.bind(this);
  }

  addTodo(e) {
    e.preventDefault();
    interface Item {
      id: number;
      title: string;
      isDone: boolean;
    }

    const newTodos = this.state.todos.slice();
    const item: Item = {
      id: new Date().getTime(),
      title: this.state.item,
      isDone: false
    }
    newTodos.push(item);
    this.setState({
      todos: newTodos
      item: ''
    })
  }

  updateItem(e) {
    this.setState({
      item: e.target.value,
    })
  }
  render() {
    const todos = this.state.todos.map ( todo => {
      return (<li key={ todo.id }>{ todo.title } : { todo.id } : { String(todo.isDone) }</li>)
    })

    return(
      <div className="container">
        <TodoForm
          item={ this.state.item }
          updateItem={ this.updateItem }
          addTodo={ this.addTodo }
        />
        <ul>
          { todos }
        </ul>
      </div>
    )
  }
}

export default App;

todoList 関数化

import * as React from 'react';

const todos = [
  {id: 0, title: "Task 0", isDone: false },
  {id: 1, title: "Task 1", isDone: true },
  {id: 2, title: "Task 2", isDone: false },
]

function TodoList (props) {
  const todos = props.todos.map( todo => {
    return (<li key={ todo.id }>
      { todo.title } : { todo.id } : { String(todo.isDone) }
      </li>)
  });
  return(
    <ul>
      { todos }
    </ul>
  )
};

function TodoForm(props) {
  return(
    <form onSubmit={ props.addTodo }>
      <input type="text"
        value={ props.item }
        onChange={ props.updateItem }
      />
      <input type="submit" value="Add"/>
    </form>
  )

}

class App extends React.Component {
  private state: any;
  private setState: any;
  constructor() {
    super();
    this.state = {
      todos: todos,
      item: '',
    }
    this.addTodo = this.addTodo.bind(this);
    this.updateItem = this.updateItem.bind(this);
  }

  addTodo(e) {
    e.preventDefault();
    interface Item {
      id: number;
      title: string;
      isDone: boolean;
    }

    const newTodos = this.state.todos.slice();
    const item: Item = {
      id: new Date().getTime(),
      title: this.state.item,
      isDone: false
    }
    newTodos.push(item);
    this.setState({
      todos: newTodos
      item: ''
    })
  }

  updateItem(e) {
    this.setState({
      item: e.target.value,
    })
  }
  render() {
    // const todos = this.state.todos.map ( todo => {
    //   return (<li key={ todo.id }>{ todo.title } : { todo.id } : { String(todo.isDone) }</li>)
    // })

    return(
      <div className="container">
        <TodoForm
          item={ this.state.item }
          updateItem={ this.updateItem }
          addTodo={ this.addTodo }
        />
        <TodoList todos={ this.state.todos } />
        {/* <ul>
          { todos }
        </ul> */}
      </div>
    )
  }
}

export default App;

checkbox, deleteTodo 処理

App.tsx

import * as React from 'react';

const todos = [
  {id: 0, title: "Task 0", isDone: false },
  {id: 1, title: "Task 1", isDone: true },
  {id: 2, title: "Task 2", isDone: false },
]

function TodoList (props) {
  const todos = props.todos.map( todo => {
    return (<li key={ todo.id } className={ todo.isDone ? 'done' : ''}>

      <input type="checkbox"
       onChange={ () => {props.changeCheckbox(todo)} }
       checked={ todo.isDone ? "chacked" : ""}
      />

      { todo.id } : { todo.title } : { String(todo.isDone) }

      <span className="delete" onClick={ () => {props.deleteTodo(todo.id)} } >[x]</span>

      </li>)
  });
  return(
    <ul>
      { todos }
    </ul>
  )
};

function TodoForm(props) {
  return(
    <form onSubmit={ props.addTodo }>
      <input type="text"
        value={ props.item }
        onChange={ props.updateItem }
      />
      <input type="submit" value="Add"/>
    </form>
  )

}


class App extends React.Component {
  private state: any;
  private setState: any;
  constructor() {
    super();
    this.state = {
      todos: todos,
      item: '',
    }
    this.addTodo = this.addTodo.bind(this);
    this.updateItem = this.updateItem.bind(this);
    this.deleteTodo = this.deleteTodo.bind(this);
    this.changeCheckbox = this.changeCheckbox.bind(this);
  }

  changeCheckbox(todo) {
    console.log("changeCheckbox ...");
    const newTodos = this.state.todos.slice();
    const pos = newTodos.map( todo => {
      return todo.id
    }).indexOf(todo.id)
    newTodos[pos].isDone = !newTodos[pos].isDone

    this.setState({
      todos: newTodos
    })
  }

  deleteTodo(id) {
    // console.log("deleteBtn");
    const newTodos = this.state.todos.slice();
    const pos = newTodos.map( todo => {
      return todo.id
    }).indexOf(id)
    // console.log(pos);
    newTodos.splice(pos,1)

    this.setState({
      todos: newTodos
    })
  }

  addTodo(e) {
    e.preventDefault();
    interface Item {
      id: number;
      title: string;
      isDone: boolean;
    }

    const newTodos = this.state.todos.slice();
    const item: Item = {
      id: new Date().getTime(),
      title: this.state.item,
      isDone: false
    }
    newTodos.push(item);
    this.setState({
      todos: newTodos
      item: ''
    })
  }

  updateItem(e) {
    this.setState({
      item: e.target.value,
    })
  }
  render() {
    return(
      <div className="container">
        <TodoForm
          item={ this.state.item }
          updateItem={ this.updateItem }
          addTodo={ this.addTodo }
        />
        <TodoList
         todos={ this.state.todos }
         deleteTodo={ this.deleteTodo }
         changeCheckbox={ this.changeCheckbox }
         />
      </div>
    )
  }
}

export default App;

todoList を todoItem でさらに分割

todoListの分割は好みです。 関数の見通しが悪くなりそうだったら、分割したらいいかと思います。
バケツリレーがもれなくついてくるので、子要素は程々に

import * as React from 'react';

const todos = [
  {id: 0, title: "Task 0", isDone: false },
  {id: 1, title: "Task 1", isDone: true },
  {id: 2, title: "Task 2", isDone: false },
]
function TodoItem(props) {
  const todo = props.todo;
  return (<li className={ todo.isDone ? 'done' : ''}>

    <input type="checkbox"
      onChange={ () => {props.changeCheckbox(todo)} }
      checked={ todo.isDone ? "chacked" : ""}
    />

    { todo.id } : { todo.title } : { String(todo.isDone) }

    <span className="delete" onClick={ () => {props.deleteTodo(todo.id)} } >[x]</span>

    </li>)
}

function TodoList (props) {
  const todos = props.todos.map( todo => {
    return <TodoItem
      key={ todo.id }
      todo={ todo }
      changeCheckbox={ props.changeCheckbox }
      deleteTodo={ props.deleteTodo }
      />
  });
  return(
    <ul>
      { todos }
    </ul>
  )
};

// function TodoList (props) {
//   const todos = props.todos.map( todo => {
//     return (<li key={ todo.id } className={ todo.isDone ? 'done' : ''}>

//       <input type="checkbox"
//        onChange={ () => {props.changeCheckbox(todo)} }
//        checked={ todo.isDone ? "chacked" : ""}
//       />

//       { todo.id } : { todo.title } : { String(todo.isDone) }

//       <span className="delete" onClick={ () => {props.deleteTodo(todo.id)} } >[x]</span>

//       </li>)
//   });
//   return(
//     <ul>
//       { todos }
//     </ul>
//   )
// };

function TodoForm(props) {
  return(
    <form onSubmit={ props.addTodo }>
      <input type="text"
        value={ props.item }
        onChange={ props.updateItem }
      />
      <input type="submit" value="Add"/>
    </form>
  )

}


class App extends React.Component {
  private state: any;
  private setState: any;
  constructor() {
    super();
    this.state = {
      todos: todos,
      item: '',
    }
    this.addTodo = this.addTodo.bind(this);
    this.updateItem = this.updateItem.bind(this);
    this.deleteTodo = this.deleteTodo.bind(this);
    this.changeCheckbox = this.changeCheckbox.bind(this);
  }

  changeCheckbox(todo) {
    console.log("changeCheckbox ...");
    const newTodos = this.state.todos.slice();
    const pos = newTodos.map( todo => {
      return todo.id
    }).indexOf(todo.id)
    newTodos[pos].isDone = !newTodos[pos].isDone

    this.setState({
      todos: newTodos
    })
  }

  deleteTodo(id) {
    // console.log("deleteBtn");
    const newTodos = this.state.todos.slice();
    const pos = newTodos.map( todo => {
      return todo.id
    }).indexOf(id)
    // console.log(pos);
    newTodos.splice(pos,1)

    this.setState({
      todos: newTodos
    })
  }

  addTodo(e) {
    e.preventDefault();
    interface Item {
      id: number;
      title: string;
      isDone: boolean;
    }

    const newTodos = this.state.todos.slice();
    const item: Item = {
      id: new Date().getTime(),
      title: this.state.item,
      isDone: false
    }
    newTodos.push(item);
    this.setState({
      todos: newTodos
      item: ''
    })
  }

  updateItem(e) {
    this.setState({
      item: e.target.value,
    })
  }
  render() {
    return(
      <div className="container">
        <TodoForm
          item={ this.state.item }
          updateItem={ this.updateItem }
          addTodo={ this.addTodo }
        />
        <TodoList
         todos={ this.state.todos }
         deleteTodo={ this.deleteTodo }
         changeCheckbox={ this.changeCheckbox }
         />
      </div>
    )
  }
}

export default App;

一括削除 purge

import * as React from 'react';

const todos = [
  {id: 0, title: "Task 0", isDone: false },
  {id: 1, title: "Task 1", isDone: true },
  {id: 2, title: "Task 2", isDone: false },
]
function TodoItem(props) {
  const todo = props.todo;
  return (<li className={ todo.isDone ? 'done' : ''}>

    <input type="checkbox"
      onChange={ () => {props.changeCheckbox(todo)} }
      checked={ todo.isDone ? "chacked" : ""}
    />

    { todo.id } : { todo.title } : { String(todo.isDone) }

    <span className="delete" onClick={ () => {props.deleteTodo(todo.id)} } >[x]</span>

    </li>)
}

function TodoList (props) {
  const todos = props.todos.map( todo => {
    return <TodoItem
      key={ todo.id }
      todo={ todo }
      changeCheckbox={ props.changeCheckbox }
      deleteTodo={ props.deleteTodo }
      />
  });
  return(
    <ul>
      { todos }
    </ul>
  )
};

function TodoForm(props) {
  return(
    <form onSubmit={ props.addTodo }>
      <input type="text"
        value={ props.item }
        onChange={ props.updateItem }
      />
      <input type="submit" value="Add"/>
    </form>
  )

}

function TodoTitle(props) {
  return (
    <h1>
      Todo
      <button className="purge" onClick={ props.todoPurge }>Purge</button>
    </h1>
   )
}


class App extends React.Component {
  private state: any;
  private setState: any;
  constructor() {
    super();
    this.state = {
      todos: todos,
      item: '',
    }
    this.addTodo = this.addTodo.bind(this);
    this.updateItem = this.updateItem.bind(this);
    this.deleteTodo = this.deleteTodo.bind(this);
    this.changeCheckbox = this.changeCheckbox.bind(this);
    this.todoPurge = this.todoPurge.bind(this);
  }

  todoPurge() {
    const todos = this.state.todos.slice();
    const newTodos = todos.filter( todo => {
      return !todo.isDone;
    })

    this.setState({
      todos: newTodos
    })
  }

  changeCheckbox(todo) {
    const newTodos = this.state.todos.slice();
    const pos = newTodos.map( todo => {
      return todo.id
    }).indexOf(todo.id)
    newTodos[pos].isDone = !newTodos[pos].isDone

    this.setState({
      todos: newTodos
    })
  }

  deleteTodo(id) {
    // console.log("deleteBtn");
    const newTodos = this.state.todos.slice();
    const pos = newTodos.map( todo => {
      return todo.id
    }).indexOf(id)
    // console.log(pos);
    newTodos.splice(pos,1)

    this.setState({
      todos: newTodos
    })
  }

  addTodo(e) {
    e.preventDefault();
    interface Item {
      id: number;
      title: string;
      isDone: boolean;
    }

    const newTodos = this.state.todos.slice();
    const item: Item = {
      id: new Date().getTime(),
      title: this.state.item,
      isDone: false
    }
    newTodos.push(item);
    this.setState({
      todos: newTodos
      item: ''
    })
  }

  updateItem(e) {
    this.setState({
      item: e.target.value,
    })
  }
  render() {
    return(
      <div className="container">
        <TodoTitle todoPurge={ this.todoPurge } />
        <TodoForm
          item={ this.state.item }
          updateItem={ this.updateItem }
          addTodo={ this.addTodo }
        />
        <TodoList
         todos={ this.state.todos }
         deleteTodo={ this.deleteTodo }
         changeCheckbox={ this.changeCheckbox }
         />
      </div>
    )
  }
}

export default App;

firebase / fireStore

fireStore を 組み込んでいます。
Vue.js編 と似ているので、同じように組み込んで見てください.
ただ, FireStore の組み込み方が React の方法に遵守できていないように思います。
setState が からんでいない。。。
ここは、また更新するかも。

  • コンテナ linux
uname
cd src
yarn add firebase
parcel --hmr-port 1235 --hmr-hostname localhost index.pug

firebase.js

export const config = {
  apiKey: "",
  authDomain: "",
  databaseURL: "",
  projectId: "",
  storageBucket: "",
  messagingSenderId: "",
  appId: "",
  measurementId: ""
};

React の 一方通行ルールは、 Vue.js をした後では、辛いですね。
input 処理書くだけでも、正直面倒くさい(笑)
setState を必ず使う必要があるのも、初心者キラーでコードも増えがちです。
個人でちょこちょこ書くなら, Vue.js の方が楽しいかと思います。 チームプレイで他のコードも見ないとだめなら、ルールの厳しいReact のほうがコードを追いやすいのかなと思います。

src/App.tsx

import * as React from 'react';
import * as firebase from 'firebase/app';
import 'firebase/firestore';
import { config } from '../firebase';

firebase.initializeApp(config);
const firestore = firebase.firestore()
const collection = firestore.collection('reactTodo');

function TodoItem(props) {
  const todo = props.todo;
  return (<li className={ todo.isDone ? 'done' : ''}>

    <input type="checkbox"
      onChange={ () => {props.changeCheckbox(todo)} }
      checked={ todo.isDone ? "chacked" : ""}
    />

    { todo.title } : { String(todo.isDone) } : { todo.id } : { todo.dbId }

    <span className="delete" onClick={ () => {props.deleteTodo(todo)} } >[x]</span>

    </li>)
}

function TodoList (props) {
  const todos = props.todos.map( todo => {
    return <TodoItem
      key={ todo.id }
      todo={ todo }
      changeCheckbox={ props.changeCheckbox }
      deleteTodo={ props.deleteTodo }
      />
  });
  return(
    <ul>
      { todos }
    </ul>
  )
};

function TodoForm(props) {
  return(
    <form onSubmit={ props.addTodo }>
      <input type="text"
        value={ props.item }
        onChange={ props.updateItem }
      />
      <input type="submit" value="Add"/>
    </form>
  )
}

function TodoTitle(props) {
  return (
    <h1>
      Todo
      <button className="purge" onClick={ props.todoPurge }>Purge</button>
    </h1>
   )
}


class App extends React.Component {
  private state: any;
  private setState: any;
  constructor() {
    super();
    this.state = {
      todos: [],
      itemsId: [],
      item: '',
    }
    this.addTodo = this.addTodo.bind(this);
    this.updateItem = this.updateItem.bind(this);
    this.deleteTodo = this.deleteTodo.bind(this);
    this.changeCheckbox = this.changeCheckbox.bind(this);
    this.todoPurge = this.todoPurge.bind(this);
  }

  async todoPurge() {
    console.log("purge...");
    const todos = this.state.todos.slice();
    const newTodos = todos.filter( todo => {
      return !todo.isDone;
    })

    this.setState({
      todos: newTodos
    })

      const itemsQuery = await collection.where("isDone", "==" , true)
        .get()
        .then(function(querySnapshot) {
          querySnapshot.forEach(function(doc) {
            const item = collection.doc(doc.id).delete();
            console.log(item);
          });
        })
        .catch(function(error) {
          console.log("Error getting documents: ", error);
        })


  }

  async changeCheckbox(todo) {
    // const newTodos = this.state.todos.slice();
    // const pos = newTodos.map( todo => {
    //   return todo.id
    // }).indexOf(todo.id)

    // newTodos[pos].isDone = !newTodos[pos].isDone
    // this.setState({
    //   todos: newTodos
    // })

    // setStateを使っていないので、ルール違反だが、簡潔に処理する方法が思いつかなかったよ
    await collection.doc(todo.dbId).update({
      isDone: !todo.isDone
    });
  }

  async deleteTodo(deleteTodo) {
    const newTodos = this.state.todos.slice();
    const pos = newTodos.map( todo => {
      return todo.id
    }).indexOf(deleteTodo.id)

    // なくても動くが、React ルール上付けている
    newTodos.splice(pos,1)
    this.setState({
      todos: newTodos,
    })

    await collection.doc(deleteTodo.dbId).delete();
  }

  async addTodo(e) {
    e.preventDefault();
    if (!this.state.item.trim()) return

    interface Item {
      id: number;
      title: string;
      isDone: boolean;
      created_at: any;
    }

    const newTodos = this.state.todos.slice(); // todos Deep Copy
    const item: Item = {
      id: new Date().getTime(),
      title: this.state.item,
      isDone: false,
      created_at: firebase.firestore.FieldValue.serverTimestamp()
    }
    newTodos.push(item);
    this.setState({
      todos: newTodos,
      item: '',
    })

    await collection.add(item)
      .then( (doc) => {
        console.log('Writed: ',doc.id);
      })
      .catch( err => {
        console.log(err);
      })
  }
  async componentDidUpdate() { // 更新用
    const query = await collection.orderBy("created_at", "desc").get()
    const itemsData = query.docs.map( item => {
      return ({
        dbId: item.id,
        id: item.data().id,
        title: item.data().title,
        isDone: item.data().isDone,
        created_at: item.data().created_at,
        date: item.data().date,
      })
    })
    this.setState({
      todos: itemsData,
    })
  }
  async componentDidMount() { // リロード用
    const query = await collection.orderBy("created_at", "desc").get()
    const itemsData = query.docs.map( item => {
      return ({
        dbId: item.id,
        id: item.data().id,
        title: item.data().title,
        isDone: item.data().isDone,
        created_at: item.data().created_at,
        date: item.data().date,
      })
    })
    this.setState({
      todos: itemsData,
    })
  }

  updateItem(e) { // input用
    this.setState({
      item: e.target.value,
    })
  }
  render() {
    return(
      <div className="container">
        <TodoTitle todoPurge={ this.todoPurge } />
        <TodoForm
          item={ this.state.item }
          updateItem={ this.updateItem }
          addTodo={ this.addTodo }
        />
        <TodoList
         todos={ this.state.todos }
         deleteTodo={ this.deleteTodo }
         changeCheckbox={ this.changeCheckbox }
         />
      </div>
    )
  }
}

export default App;

React 編 終了です。
React って便利! を実感するために、VanillaJS(素のJS) も予定しています。

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?