20210315のReactに関する記事は22件です。

とりあえず React + TypeScript で適当なサンプルページを作りたい ③

この記事は未完成の記事です。筆者の性格上、完成してから公開するよりも、公開して追記とメンテナンスするほうが書物が継続するので公開しています。

これまで

前回
https://qiita.com/KennyKTA/items/9e240cd90ecb86ea3ee5
前々回
https://qiita.com/KennyKTA/items/c05c5047953777eeb5c4

今回やること

  • React Boot Strap の Getting Start のLayout のセクションを理解する。
  • React + Boot Strap によるグリッドシステムの実装を行う。

今回やらないこと

  • React Boot Strap の Getting Start のLayout の完全な翻訳はしない。自分に必要な範囲で訳し、記録する。
  • flexbox に関する調査、言及はしない。

開発内容

まずは React Boot Strap のGetting started のGrid のセクションを軽く読んでいく。

全てに関して実施しないが、備忘とMarkdownの練習を目的として上記ページの引用 + 翻訳を行う。区切りはMarkdownの水平線を利用してみる。


Grid system

Bootstrap’s grid system uses containers, rows, and columns to layout and align content. It’s built with flexbox and is fully responsive. Below is an example and an in-depth look at how the grid comes together.

Bootstrap のグリッドシステムはレイアウトと整列を行うためにコンテナ、ロウ、カラムを利用します。この技術はflexboxをもとに構築されており、完全レスポンシブです。以下が、グリッドがどのように組み合わされるかの例と詳細です。

(どうやらflexbox と呼ばれる技術の上にグリッドシステムが構築されているらしい。上述の引用からもflexboxにはリンクが張られており、理解した方がよいのだろうが深入りせず飛ばしていく)。


Container

Containers provide a means to center and horizontally pad your site’s contents. Use Container for a responsive pixel width.

コンテナはあなたのサイトコンテンツを中央に配置し水平方向に並べるための手段を提供します。レスポンシブなピクセル幅を実現するためにはコンテナを利用してください。

(日本語だけだと理解ができない。水平方向に並べるとあるので、Containerの中に何か並べるのだろう。サンプルコードではRow が1つ並べられている。つまり1行だ。)


Fluid Container

You can use for width: 100% across all viewport and device sizes.

Container fluid を利用することで全てのviewport とデバイスサイズに対して幅100% を実現できます。
(訳があっている自信がない、、、viewport ってなんだ?いずれにせよContainer にはfluid というpropが存在するらしい)

You can set breakpoints for the fluid prop. Setting it to a breakpoint (sm, md, lg, xl) will set the Container as fluid until the specified breakpoint.

fluid prop にはブレークポイントを設定することが可能です。ブレークポイント(sm,md,lg,xl)を設定することでContainer は 指定したブレークポイントまで流動的になります。

(訳しても意味がわからない。推測するに、fluid にデバイスサイズ?か何かを渡すとレスポンシブに表示がなされるということなのだろう。指定サイズまではコンテナが伸びるとか、そんなのじゃないかな?後で試してみよう。)


Auto-layout columns

When no column widths are specified the Col component will render equal width columns

列の幅に対して指定がない場合、Col コンポーネントは均等な幅で列を描画します。
(同一行の列に幅指定がなければ、等間隔の列が描画される。直観的でわかりやすい。)


Setting one column width

明日はここから調べて書く。


その他

今のところ、特になし。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

とりあえず React + TypeScript で適当なサンプルページを作りたい ②

前回

今回やること

  • React + Boot Strap でなにがしかの実装を行う。
  • React + Boot Strap によるグリッドシステムの実装を行う(次回に譲るかも)。

補足:
軽く調べるとレイアウトの配置はグリッドシステムというものを使うそうだ。別の技術もあり得るだろうが、今回は一通り走ることが大目標なので、これを採用する。Boot Strap という聞いたことのある名前の技術がReactにグリッドシステムを提供しているようなのでこれをターゲットに実装する。

今回やらないこと

  • 特になし

開発

  • React BootStrap の公式ページのGet Start に従っていく。

React Boot Strap のパッケージをインストール

npm install react-bootstrap bootstrap

アプリのソースを以下のように実装。

App.tsx
import React from 'react';
import logo from './logo.svg';
import './App.css';
import { LanguageServiceMode } from 'typescript';
import { Button } from 'react-bootstrap';

function App() {
  return (
    <div>
      <h1>this is sample</h1>
      <Button>this is sample <button type="submit"></button></Button>
    </div>
  );
}

export default App;

ローカルサーバの立ち上げを実施。

npm start

以下のようなページが表示された。
image.png

どうやらReact Boot Strap はUIコンポーネントも提供しているらしい。かなり色々できそうだが本丸はグリッドシステムなので、そこを重点的に調べる。グリッドシステムに関しては次回に譲る。

その他

  • グリッドシステムを調べる過程で面白そうなものを見つけた。別の機会に調べたい。
    https://github.com/react-grid-layout/react-grid-layout

  • React の公式から辿って知ったのだがCodeSandboxというWebサービスがある。
    https://codesandbox.io/
    node.js のIDE環境で、作成したReactアプリを簡易にホストしてくれるらしい。
    開発環境を構築する手間が省けるし、npm start のようなサーバ立ち上げのコマンドも不要だ。
    実装の挙動だけを簡単に確認したい場合はこれでもいい気がしてきた。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

とりあえず React + TypeScript で適当なサンプルページを作りたい

目的

  • 記事を作成することでMarkdownの仕様に慣れる。
  • React と TypeScript に慣れる。

やること

  • とにかくReact + TypeScript でサンプルページを作る。
  • まずGUIをレイアウトできるようする。
  • 使えそうなComponentは持ってきて利用する。

やらないこと

  • 適切なコンポネントの切り分けはしない。
  • 適切なprops の伝搬はしない。
  • Redux などの設計思想の取り込みはしない。
  • GUIに紐づく処理の開発はしない。 (とにかく目に見えるものだけ作ってみる)

設計

とにかく作るといっても作るもののイメージがないことには始まらないので、作成対象を設計した。設計はQiitaのブログを表示した際のレイアウトを参考にした。
QiitaBluePrint.png

上記をもとに開発を進めていこうと思う。この記事は意思表示とレイアウト作成までとし、ここまでで終了する。記事も小分けに書いていかないと、大作化してしまい結局最後まで書かないと考えているからだ。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

とりあえず React + TypeScript で適当なサンプルページを作りたい ①

目的

  • 記事を作成することでMarkdownの仕様に慣れる。
  • React と TypeScript に慣れる。

やること

  • とにかくReact + TypeScript でサンプルページを作る。
  • まずGUIをレイアウトできるようする。
  • 使えそうなComponentは持ってきて利用する。

やらないこと

  • 適切なコンポネントの切り分けはしない。
  • 適切なprops の伝搬はしない。
  • Redux などの設計思想の取り込みはしない。
  • デザインの美しさは問わない。
  • レスポンシブ対応はしない。
  • GUIに紐づく処理の開発はしない。とにかく目に見えるものだけ作ってみる。

やらないことは事前に割り切って決めたほうがよい。走りきることが大事で、目移りしないため工夫だ。そのため妥協点をやらないこととして後で追加するかもしれない。

設計

とにかく作るといっても作るもののイメージがないことには始まらないので、作成対象を設計した。設計はQiitaのブログを表示した際のレイアウトを参考にした。
QiitaBluePrint.png

上記をもとに開発を進めていこうと思う。この記事は意思表示とレイアウト作成までとし、ここまでで終了する。記事も小分けに書いていかないと、大作化してしまい結局最後まで書かないと考えているからだ。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reduxの基本をまとめてみた

はじめに

主に公式のチュートリアルを参照しながらReduxを勉強したので簡単に記事にまとめようと思います。
(React公式もそうですが、フロント系の公式ドキュメントは記載がとても丁寧で助かりますね。)
もし誤り等ありましたらコメントでご指摘いただけますと助かります :bow:

Reduxとは?

UIが持つ状態(state)を管理するためのJSのライブラリです。(VueやAngularに使うこともできますが、Reactと最も相性が良いです。)
Reactのようなフロントのフレームワークにおいてコンポーネントのstateをどのように管理するかは、パフォーマンスやメンテナンス性にも関わる重要な命題であると思います。(僕自身よく頭を悩ませます。。)
Hooks(useReducer)を使用した管理方法などもありますが、Reduxはstoreというコンポーネントとは独立した場所でグローバルにstateを管理するライブラリです。基本的にstateの管理はローカルに行い、異なるコンポーネント間でstateを共有するときは共通の祖先コンポーネントまで遡るのが一般的なやり方かと思いますが、規模の大きなアプリケーションでstate管理がつらくなってきたときに導入を検討する類のライブラリのようです。
公式も「様々な場所に複雑な処理を行うstateが散りばめられた大きなアプリケーションで効果を発揮するライブラリである」みたいなことを言っており、Not all apps need Redux とドキュメントに明記されています。

基本的な流れ

Reduxでは、stateを管理するstoreに対し、処理に関する情報を持ったactionをdispatch(「送り込む/割り当てる」みたいなイメージ持っています)することにより、stateを更新します。
storeの中では、reducerという関数がdispatchされたactionを解釈し、更新した新しいstateを返します。

action

アプリケーションに対する何かしらのイベントを表すもので、ユーザーによる操作をトリガーに生成されます。
実態はオブジェクトで、イベントの性質を表すtypeプロパティを持ちます。例えば、countというstateの数字に1を追加するようなactionであった場合、 { type: 'increment_count' } のようなイメージです。

store

stateをオブジェクトとして管理します。後述のreducerを渡して生成します。
既存のstateには、getStateメソッドをかけることでアクセスすることができます。
そしてこのstoreがdispatchメソッドを持っており、actionを引数にdispatchすることでstateの操作を行うことができます。その具体的な処理については、後述のreducerで行います。

reducer

現在のstateとactionを引数として受け取り、stateを返す関数です。storeの中で機能します。
actionを解釈してstateの更新が必要であれば新しいstateを生成して返し、更新が必要なければ既存のstateをそのまま返すのが基本動作です。

上述の要素がどのように関連しあってstateを更新しているかの流れは公式のフロー図がとっても分かりやすいです。

①ユーザーからの入力(ボタンを押すなど)がある
②actionがstoreにdispatchされる
③storeの中ではreducerが渡されたactionに基づいてstateを更新する
④新しいstateに基づいてUIが再レンダリングされる

といった流れです。

簡単な実例

create-react-app で作ったReactプロジェクトの簡単な実例です。
Upボタンを押せば表示の数字がカウントアップされ、Resetボタンを押せば0に初期化されるだけのごくごくシンプルなアプリケーションです。

まずはsrc/index.jsファイルです。
ルートコンポーネントであるAppコンポーネントをreact-redux提供のProviderコンポーネントでラップし、Providerコンポーネントにインポートしたstoreを渡しております。このstoreに対して、既存stateの取得なりactionのdispatchなりをしていくことになります。

src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import store from './app/store';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

次に、そのstoreを定義しているsrc/app/store.jsファイルです。
簡略的にreducerをここで定義し、store作成時に渡しています。
reducerではaction.typeを見て処理を判断し、stateを更新する必要がある場合は、新たにstateオブジェクトを生成して返しています。(既存のstate自体を変更するのでなく、新しいstateオブジェクトを生成して返すことがreducer関数では重要です。)

src/app/store.js
import { createStore } from 'redux';

const initialState = {
  count: 0,
};

const reducer = (state = initialState, action) => {
  switch(action.type) {
    case 'increment_count':
      return {
        count: state.count + 1,
      };
    case 'reset_count':
      return {
        count: 0,
      };
    default:
      return state;
  }
};

const store = createStore(reducer);

export default store;

次に、このように作成してReactと紐づけたstoreに対してどのようにアクセスするかですが、src/App.jsを見てみましょう。
storeへのアクセスの方法のひとつとして、useSelectorとuseDispatchというHooksを利用することができます。
storeのstateの取得にはuseSelectorが使えます。引数ではどのstateが欲しいか指定する関数(selector)を渡します。またこのuseSelectorはstoreのstateの更新/差分を感知し、再レンダリングを行なってくれます。
useDispatchはstoreへのdispatchメソッドを提供してくれます。actionを引数にこれを呼べば、storeへdispatchすることができます。

src/App.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';

function App() {
  const count = useSelector((state) => state.count);
  const dispach = useDispatch();

  const increment = () => {
    dispach({ type: 'increment_count' });
  };
  const reset = () => {
    dispach({ type: 'reset_count' });
  };

  return (
    <div className="App">
      <p>Counter: {count}</p>
      <button onClick={increment}>Up</button>
      <button onClick={reset}>Reset</button>
    </div>
  );
}

export default App;

まとめ

Reduxの基本的な処理の流れを理解する良い機会になりました。React経験をもっと積んでケースに応じた適切なstate管理方法を選択できるようになりたいものです。

参考

https://redux.js.org
https://ics.media/entry/200409/
https://reffect.co.jp/react/react-redux-for-beginner

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Django REST Framework]でReactとのAPI連携

実行環境

MacOS BigSur -- 11.2.1
Python3 -- 3.8.2
Django -- 3.1.7
djangorestframework -- 3.12.2
npm -- 6.14.4
react -- 17.0.1
react-dom -- 17.0.1
axios -- 0.21.1

Django REST Framework(DRF) を始めてみる

これまでDjangoのみを用いたWebアプリケーションの作成をしていましたが、フロントエンドの高度化ができず、DRFを用いてReactと組み合わせてみたい!!!と思いました。Reactに関してはまだ知識が少ないのですが、とりあえずDRFで作成したAPIをReactに渡す部分を実装をしたのでその手順をメモがてらに記述します。

DRFの続き(バックエンド)

前回の記事⇨
https://qiita.com/kachuno9/items/52a756f15207f625f358
でカスタムユーザーによるDRFプロジェクト作成は終わっているのでその続きからです。
ちなみに、ディレクトリ構成はこんな感じです。

my_api
   ├── backend
   │   ├── __init__.py
   │   ├── admin.py
   │   ├── apps.py
   │   ├── migrations
   │   ├── models.py
   │   ├── serializers.py
   │   ├── tests.py
   │   ├── urls.py
   │   └── views.py
   ├── db.sqlite3
   ├── manage.py
   ├── media
   └── my_api
       ├── __init__.py
       ├── asgi.py
       ├── settings.py
       ├── urls.py
       └── wsgi.py

Model

とりあえず簡単にPostモデルを作成しました。

models.py
class Post(models.Model):
    title = models.CharField('タイトル', max_length=50)
    text = models.TextField('テキスト')
    created_at = models.DateField('作成日', auto_now_add=True)
    updated_at = models.DateField('更新日', auto_now=True)

    def __str__(self):
        return self.title

View

views.py
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from backend import serializers
from .models import Post

class PostAll(APIView):
    def get(self, request):
        try:
            post = Post.objects.all().order_by('-created_at')
            res_list = [
                {
                    'id': p.id,
                    'date': p.created_at,
                    'title': p.title,
                }
                for p in post
            ]
            return Response(res_list)
        except:
            return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)

URL関連

さて、簡単なモデルとビューを作成したのでURLを設定します。

my_api/urls.py
from django.contrib import admin
from django.urls import path, include
from django.conf import settings

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/', include('backend.urls'))
]
backend/urls.py
from django.urls import path
from backend import views

urlpatterns = [
    path('', views.PostAll.as_view()),
]

動作確認

仮想環境内で、サーバーを立ち上げます。

$ python3 manage.py runserver

http://localhost:8000/api/v1/
にアクセスすると、しっかりとAPIの動作確認が行えました。
ちなみにデータはadminサイトであらかじめサンプルようにいくつか追加しています。
スクリーンショット 2021-03-15 13.26.40.png

Reactプロジェクト作成(フロント)

先程までとは別のターミナルを立ち上げて環境構築、プロジェクト作成を行います。アプリ名は「frontend」にしました。

$ npx create-react-app
$ cd frontend
$ npm install axios
$ npm install react-router-dom
$ npm start

するとReacrのロゴが表示されるページが出てきて、正常にプロジェクト作成できた事がわかります。
ちなみに、frontend/srcのディレクトリ構成はこの通りです。

frontend/src
   ├── App.css
   ├── App.js
   ├── App.test.js
   ├── index.css
   ├── index.js
   ├── reportWebVitals.js
   └── setupTests.js

ReactとDRFの連携

さて、いよいよDRFとの連携を行います。DRFでは
http://localhost:8000/api/v1/
でデータを取得できるようにしているので、React側でここにGetリクエストを送るように実装します。axiosを用いることで簡単にリクエストを実装できるということで、インストールしました。

App.js
import React, { Component } from 'react';
import axios from 'axios';

class App extends Component {
    state = {
        posts: []
    };

    componentDidMount() {
        this.getPosts();
    }

    getPosts() {
        axios
            .get('http://localhost:8000/api/v1/')
            .then(res => {
                this.setState({ posts: res.data });
            })
            .catch(err => {
                console.log(err);
            });
    }

    render() {
        return (
            <div>
                {this.state.posts.map(item => (
                     <div key={item.id}>
                        <h1>{item.title}</h1>
                        <p>{item.date}</p>
                     </div>
                ))}
            </div>
        );
    }
}

export default App;

http://localhost:3000/
にアクセスするとしっかりDRFからデータを受け取っている事が分かります!!
スクリーンショット 2021-03-15 17.54.23.png

今回はDRFとReactの連携が確認できたところまでです。
これまでバックエンドとフロントエンドを分けて開発するといった高度な開発経験がなかったので、DRFとReactが連携できただけですごく嬉しかったです(笑)
Reactに関してはまだまだ知識が少なく、見よう見まねですがこれから開発を進めていきたいと思います!!

参考

以下のページが非常に分かりやすく、参考にさせていただきました。
- https://qiita.com/__init__/items/f5a5a64a05541fcda713#todo-api-%E3%81%AE%E6%A7%8B%E7%AF%89
- https://qiita.com/Piyopanman/items/5ffb9c290d452e0a5782
- https://qiita.com/Piyopanman/items/0a757a7b2dd412e07135

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[react-calendar-heatmap] ReactでGitHubの草(カレンダー)を作る

スクリーンショット 2021-03-15 16.45.28.png

react-calendar-heatmapというプラグインを使ってGitHubのcommitカレンダーグラフのようなものを実装しました

環境

OS macOS Big Sur 11.2.3 11.2.3
node v14.8.0
npm 7.5.4
React 17.0.1

Reac.jsの環境構築

  • ここではreact-heatmapという名前でプロジェクトを作成しています
//Create React App
npx create-react-app react-heatmap

react-calendar-heatmapのインストール

yarn add react-calendar-heatmap
npm install react-calendar-heatmap
  • エラーが起きた場合
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR!
npm ERR! While resolving: react-heatmap@0.1.0
npm ERR! Found: react@17.0.1
npm ERR! node_modules/react
npm ERR!   react@"^17.0.1" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer react@"^16.8.0" from react-github-heatmap@1.0.7
npm ERR! node_modules/react-github-heatmap
npm ERR!   react-github-heatmap@"*" from the root project
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force, or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.

※ reactのバージョンが対応していないことが原因

npm i react-calendar-heatmap --legacy-peer-deps
  • npm installする際に、インストールするライブラリのバージョンが、インストール先のプロジェクトのバージョンに対応していない場合、 オプションで、--legacy-peer-depsを使うことで、半強制的に?installできるようになるようです。

react-tiiltipのインストール

npm install react-tooltip

ソースコード

app.js
import React from "react";
import "./App.css";
import CalendarHeatmap from "react-calendar-heatmap";
import "react-calendar-heatmap/dist/styles.css";
import ReactTooltip from "react-tooltip";

const App = () => {
  return (
    <div className="container">
      <h1>react-calendar-heatmap</h1>
      <div>
        <CalendarHeatmap
          // 表示させる月
          startDate={new Date("2016-07-01")}
          endDate={new Date("2016-12-01")}

          values={[
            { date: "2016-07-03", count: 1 },
            { date: "2016-08-22", count: 2 },
            { date: "2016-07-29", count: 4 },
            { date: '2016-10-01', count: 1 },
            { date: '2016-10-03', count: 2 },
            { date: '2016-10-06', count: 3 },
            { date: '2016-10-10', count: 4 },
            { date: '2016-10-07', count: 1 },
            { date: '2016-09-15', count: 3 },
            // ...and so on
          ]}

          // color
          classForValue={(value) => {
            if (!value) {
              return "color-empty";
            }
            return `color-scale-${value.count}`;
          }}
          tooltipDataAttrs={(value) => {
            if (!value || !value.date) {
              return null;
            }
            // react-tooltipの構成
            return {
              "data-tip": `${value.date} has count: ${
                value.count
              }`,
            };
          }}
        />
      </div>
      <ReactTooltip />
    </div>
  );
};

export default App;
app.css
.react-calendar-heatmap .color-scale-1 { fill: #d6e685; }
.react-calendar-heatmap .color-scale-2 { fill: #8cc665; }
.react-calendar-heatmap .color-scale-3 { fill: #44a340; }
.react-calendar-heatmap .color-scale-4 { fill: #1e6823; }

参考

芝の色分けを細分化

react-calendar-heatmap

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReactHooksで映画検索アプリを作る

コード

https://github.com/kenta0313/react-hooked

コード解説


1.Header.js

components/Header.js
import React from "react";

const Header = (props) => {
  return (
    <header className="App-header">
      <h2>{props.text}</h2>
    </header>
  );
};

export default Header;

親要素からレンダリングしているだけです。

index.jsのファイルをインポートするのを忘れないでください。

index.js
import App from './components/App';

2.Movie.js

conponents/Movie.js
import React from "react";

const DEFAULT_PLACEHOLDER_IMAGE =
  "https://m.media-amazon.com/images/M/MV5BMTczNTI2ODUwOF5BMl5BanBnXkFtZTcwMTU0NTIzMw@@._V1_SX300.jpg";


const Movie = ({ movie }) => {
  const poster =
    movie.Poster === "N/A" ? DEFAULT_PLACEHOLDER_IMAGE : movie.Poster;
  return (
    <div className="movie">
      <h2>{movie.Title}</h2>
      <div>
        <img
          width="200"
          alt={`The movie titled: ${movie.Title}`}
          src={poster}
        />
      </div>
      <p>({movie.Year})</p>
    </div>
  );
};


export default Movie;

こちらはAPIから取ってきた変数を引数に取っています。

3.Search.js

conponents/Search.js
import React, { useState } from "react";


const Search = (props) => {
  const [searchValue, setSearchValue] = useState("");

  const handleSearchInputChanges = (e) => {
    setSearchValue(e.target.value);
  }

  const resetInputField = () => {
    setSearchValue("")
  }

  const callSearchFunction = (e) => {
    e.preventDefault();
    props.search(searchValue);
    resetInputField();
  }

  return (
      <form className="search">
        <input
          value={searchValue}
          onChange={handleSearchInputChanges}
          type="text"
        />
        <input onClick={callSearchFunction} type="submit" value="SEARCH" />
      </form>
    );
}

export default Search;

まず、useStateで検索欄の初期値を空に設定してます。

handleSearchInputChangesで検索欄へ入力できるようにしています。(これがなかったら入力できないです。)

resetInputFieldが呼ばれると検索欄が空になりなす。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】別ファイルに関数をまとめてexportsを利用して呼び出す。

開発環境

React.js

概要

JavaScriptで関数を共通化して別ファイルにまとめたときに、HTMLのscriptタグを使い呼び出すのは面倒だと思いました。JavaScriptのexportsを使い呼び出すことができることを知りましたので記事にしておきます。

import.jsとexport.jsファイルを作成

import.jsからexport.jsの関数を呼び出していきます。

export.js
exports.criminalIsKogoro = function () {
  console.log("犯人は毛利小五郎");
}

exports.detectiveCriminal = function (name) {
  console.log("犯人は" + name);
}

const criminalIsAgasa = function () {
  console.log("犯人は阿笠だ");
}

比較するためにcriminalIsAgasaだけはexportsしないようにしておきます。

import.js
import exportFunction from 'export.jsのパスを記述'
// または
// var exportFunction = require('export.jsのパスを記述');

exportFunction.criminalIsKogoro; // "犯人は毛利小五郎"
exportFunction.detectiveCriminal("元太"); //"犯人は元太"
exportFunction.criminalIsAgasa; // error. exportFunction.criminalIsAgasa is not a function

このようにexportsをしておかないと外部のファイルから読み込むことができないことが分かりました。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React.js】別ファイルに関数をまとめてexportsを利用して呼び出す。

開発環境

React.js

概要

React.jsで関数を共通化して別ファイルにまとめたときに、HTMLのscriptタグを使い呼び出すのは面倒だと思いました。JavaScriptのexportsを使い呼び出すことができることを知りましたので、備忘録として記事に残しておきます。

import.jsとexport.jsファイルを作成

import.jsからexport.jsの関数を呼び出していきます。

export.js
exports.criminalIsKogoro = function () {
  console.log("犯人は毛利小五郎");
}

exports.detectiveCriminal = function (name) {
  console.log("犯人は" + name);
}

const criminalIsAgasa = function () {
  console.log("犯人は阿笠だ");
}

比較するためにcriminalIsAgasaだけはexportsしないようにしておきます。

import.js
import exportFunction from 'export.jsのパスを記述'
// または
// var exportFunction = require('export.jsのパスを記述');

exportFunction.criminalIsKogoro; // "犯人は毛利小五郎"
exportFunction.detectiveCriminal("元太"); //"犯人は元太"
exportFunction.criminalIsAgasa; // error. exportFunction.criminalIsAgasa is not a function

このようにexportsをしておかないと外部のファイルから読み込むことができないことが分かりました。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

laravelとreact 一緒に使うけど 厳密に言うと APIじゃない件について

めっちゃ時間無駄にしたので戒めのためのメモ

状況
laravelで作ったサービスをあとからreact化しようとした際 遭遇
axiosにて記事情報を post すると 何故か リレーションしているuser_idがnillになり処理が完了しない。

結論
・routes/api.phpにかかない
普通にweb.phpに書く

ここに書くとpostするとauthのガードに引っかかってuser_idがnillになってエラー


ちなみに私は トークン とかいろいろ 迷子になって laravel passportまでいれて結局関係にことにあとで気づきました・・。 勉強にはなったが。。。

ソース
https://teratail.com/questions/234875

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React 入門 (#2) ~Babel&JSX~

1,Babelとは

2021-03-14_14h24_31.png

ES6以降のスクリプトを、ES2015基準のスクリプトにトランスパイルする機能です。

下記のタグをheadタグに記述すれば準備完了です。

index.html
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

Babelリンク

2,JSXを使う理由

Reactはマークアップとロジックを両方含む疎結合の「コンポーネント」という単位で構成されます。
コンポーネント」は、次回説明します。
そこで活躍するのが、JSX(「JavaScript XML」の略)です。
Reactのコンポーネント内で、マークアップ言語を記述するためのXML風の構文です。

3、ソースコードを記述

前回からの続きになります。
まず、index.htmlの準備です。
code6.png

index.jsに移動して記述します。
code7.png

toUpperCase()は、すべて大文字にするメソッドです。

4,ブラウザ表示

2021-03-14_21h51_55.png
リストが登場していますね。

次回は、

コンポーネントについて扱っていきます。
次回はここへ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React入門 環境構築(for mac)(nodebrew)

まず、Homebrewをインストールします。
Homebrew公式サイト[https://brew.sh/index_ja]

公式から引用
$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
$ brew -v
#バージョンが出ればインストールできています。

Homebrewを使って、nodebrewをインストール

$ brew install nodebrew
$ nodebrew -v
#バージョンが出ればインストールできています。
$ nodebrew install-binary stable 

stableは安定バージョンです。
latestは最新バージョンです。
今回は安定バージョンを使用しています。

$ nodebrew ls
v14.16.0

current: v14.16.0

current: は使用しているバージョンです

$ nodebrew use (バージョン)

nodebrew ls実行し、反映されていたら大丈夫です。

$ vi ~/.bash_profileでしたの行を追加

export PATH=$HOME/.nodebrew/current/bin:$PATH

$ node -v$ npm -vでバージョンが出たら完了です。

$ npx create-react-app ファイル名
$ cd ファイル名
$ yarn start #起動コマンド

これで開発環境完成です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

色々めんどくさいのでDjango REST API+React/TypeScriptでアプリ作ってみる2

統合開発環境 

VSC

VSCに拡張機能を追加していきます。

・Prettier - Code formatter
・ES7 React/Redux/GraphQL/React-Native snippets

Reactのプロジェクトを作成していきます。

ターミナルに以下を入力していきます。

npx create-react-app . --template redux-typescript --use-npm

ダウンロードが完了すればHappy hacking!と帰ってきます。

Inside that directory, you can run several commands:

  npm start
    Starts the development server.

  npm run build
    Bundles the app into static files for production.

  npm test
    Starts the test runner.

  npm run eject
    Removes this tool and copies build dependencies, configuration files
    and scripts into the app directory. If you do this, you can’t go back!

We suggest that you begin by typing:

  cd C:\Users\xxx\Desktop\xxx
  npm start

Happy hacking!

続いて使用するモジュールをダウンロードしていきます。

axios,

ブラウザや node.js で動く Promise ベースの HTTP クライアントである。 REST-API を実行したいときなど、これを使うと実装が簡単にできる。

npm i axios
結果.
 axios@0.21.1
added 1 package from 1 contributor and audited 1993 packages in 12.982s

133 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

Reactのラウターということでログイン用のページとタスク管理のページをラウターで線を引いてきますので

react-rotuer-dom をインストールしていきます。

npm i react-rotuer-dom @types/react-rotuer-dom
結果.
found 0 vulnerabilities

マスターをインストール

npm i @material-ui/icore @material-ui/lab
結果.
found 0 vulnerabilities

モジュールがインストールができたらReactサーバーをスタートします。

npm start

reactTOP.PNG

立ち上げが完了しました。

社内でReactを立ち上げる際にnpx create-react-appでエラーができるときまたは、npmが通らないとき

原因=プロキシサーバー

対策↓

npm -g config set proxy プロシキ:xxx
npm -g config set https-proxy プロシキ:xxx
npm -g config set registry http://registry.npmjs.org/
npm -g config set strict-ssl=false

このようにconfigを変えてあげると create-react-appが実行できるようになります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.js開発者なら知っておきたい便利なパッケージ12選

本記事は、Indrek Lasn氏による「12 Useful Packages Every Node.js Developer Should Know」(2020年9月2日公開)の和訳を、著者の許可を得て掲載しているものです。

Node.js開発者なら知っておきたい便利なパッケージ12選

毎日の生産性を上げるNodeパッケージ

Image for post

はじめに

Node.jsはコードの再利用にぴったりです。コードを再利用するための根幹となるのはNPMパッケージです。

NPMパッケージは、時間と労力を大幅に節約してくれます。日付ライブラリが必要ですか?パッケージがあります。ユーティリティライブラリが必要ですか?問題ありません。コードの問題解決が必要な時はいつでも、必要に応じたパッケージがあるでしょう。

これは、すべてのNode.js開発者が知っておくべきパッケージのリストです。このNPMパッケージは、時間を節約し、魔法のように解決する助っ人のようなものです。

1. husky

huskyはgitフックの実装を簡単にしてくれます。チームで仕事をしていて、チーム全体にコーディング規約を適用したいと思うことはありませんか?問題ありません!huskyを使えば、リポジトリにコミットしたりプッシュしたりする前に、コードを自動的にlintしてテストするように全員に要求できます。

Image for post
husky ― https://github.com/typicode/husky

インストール方法

yarn add husky

使い方

huskyフックの実装例です。

// package.json
{
   "husky": {
      "hooks": {
         "pre-commit": "npm lint",
         "pre-push": "npm test"
      }
   }
}

huskyexample.json
huskyフック例

pre-commitフックは、リポジトリにコミットする前に実行されます。

pre-pushフックは、コードをリポジトリにプッシュする前に実行されます。

2. dotenv

dotenvは、環境変数を.envファイルからprocess.envにロードするゼロ依存モジュールです。コードとは別の環境に設定を保存することは、The Twelve-Factor Appの方法論に基づいています。
dotenv

インストール方法

yarn add dotenv

使い方

アプリケーションのできる限り早い段階で、dotenvを要求して設定します。

require('dotenv').config()

プロジェクトのルートディレクトリに.envファイルを作成します。NAME=VALUEの形式で、新しい行に環境固有の変数を追加します。

例:

DB_HOST=localhost
DB_USER=root
DB_PASS=s1mpl3

process.envで、.envファイルで定義したキーと値が使えるようになりました。

const db = require('db')
db.connect({
   host: process.env.DB_HOST,
   username: process.env.DB_USER,
   password: process.env.DB_PASS
})

3. date-fns

lodashに似ていますが、date-fnsは日付のためのものです。日付の操作をもっと簡単にするユーティリティ関数がたくさんあります。

date-fnsは、ブラウザやNode.jsでJavaScriptの日付を操作するため、総合的かつシンプルで一貫性のあるツールセットを提供します。
date-fns

Image for post
date-fns ― https://github.com/date-fns/date-fns

インストール方法

yarn add date-fns

使い方

date-fnsライブラリの簡単な例です。

import { compareAsc, format } from 'date-fns'

format(new Date(2014, 1, 11), 'yyyy-MM-dd')
//=> '2014-02-11'

const dates = [
   new Date(1995, 6, 2),
   new Date(1987, 1, 11),
   new Date(1989, 6, 10),
]
dates.sort(compareAsc)
//=> [
//    Wed Feb 11 1987 00:00:00,
//    Mon Jul 10 1989 00:00:00,
//    Sun Jul 02 1995 00:00:00
// ]

その他の事例や使用例については、ドキュメントを確認してください。

4. bunyan

bunyan は、分かりやすくてパフォーマンスが高いNode用のJSONロギングライブラリです。

Image for post
bunyan ― https://github.com/trentm/node-bunyan

インストール方法

yarn add bunyan

コツbunyan CLIツールは、すべてのバージョンのbunyanのログと(合理的な範囲内で)互換性があるようになっています。そのため、yarn add global bunyanでグローバルbunyanをインストールしてbunyan CLIをPATHに追加してから、ローカルbunyanをインストールして、アプリでbunyanのNode.jsライブラリを使うと良いでしょう。

使い方

bunyanはシンプルで高速なNode.jsサービス用のJSONロギングライブラリです。

// hi.js
const bunyan = require('bunyan');
const log = bunyan.createLogger({name: "myapp"});
log.info("hi");

node hi.jsを実行するとコンソールに返される内容は、次の通りです。

Image for post

5. ramda

rambda は JavaScript プログラマ向けの実用的で機能的なユーティリティライブラリです。rambdaは、より純粋な機能性を重視しています。

不変性と副作用のない関数は、rambdaの設計哲学の中心です。これにより、シンプルで洗練されたコードで仕事を成し遂げられます。

Image for post
rambda ― https://github.com/ramda/ramda

インストール方法

$ yarn add ramda

使い方

import * as R from 'ramda'
const greet = R.replace('{name}', R.__, 'Hello, {name}!');
greet('Alice'); //=> 'Hello, Alice!'

上のコードの例です。

6. debug

debugはNode.jsコアのデバッグ手法をモデルにした、小さなJavaScriptデバッグユーティリティです。

Image for post
debug ― https://github.com/visionmedia/debug

インストール方法

$ yarn add debug

使い方

debugには、モジュール名を渡すだけでconsole.errorの装飾版を返し、debugステートメントを渡す関数があります。

const debug = require('debug');
const log = debug('http:server');
const http = require('http');

const name = 'My App name';

log('booting %o', name);

http.createServer((req, res) => {
   log(req.method + ' ' + req.url);
   res.end('debug examplen');
}).listen(3200, () => {
   log('listening');
});

// run this command in the terminal
// DEBUG=http:server node app.js

app-debug-example.js
debug例

これによりデバッグ出力を、モジュール全体だけでなく、モジュールのさまざまな部分で切り替えられます。

Image for post

7. flat

flat は、ネストされたJavascriptオブジェクトを取得して平坦化します。区切りキーを使って、オブジェクトの平坦化を解除することもできます。

Image for post
flat ― https://github.com/hughsk/flat

インストール方法

$ yarn add flat

使い方

const flatten = require('flat')

flatten({
   key1: {
      keyA: 'valueI'
   },
   key2: {
      keyB: 'valueII'
   },
   key3: { a: { b: { c: 2 } } }
})

// {
//    'key1.keyA': 'valueI',
//    'key2.keyB': 'valueII',
//    'key3.a.b.c': 2
// }

8. JSON5

JSON5 データ交換フォーマットはJSON* のスーパーセットで、ECMAScript 5.1のプロダクションの一部を含めるために構文を拡張し、JSONの制限の一部を緩和します。
JSON5

Image for post
json5 ― https://github.com/json5/json5

インストール方法

yarn add json5
const JSON5 = require('json5')

使い方

ファイル拡張子に注意してください。JSON5は、JSONの拡張機能とスーパーセットです。

{
   // comments
   unquoted: 'and you can quote me on that',
   singleQuotes: 'I can use "double quotes" here',
   lineBreaks: "Look, Mom! \

No \\n's!",
   hexadecimal: 0xdecaf,
   leadingDecimalPoint: .8675309, andTrailing: 8675309.,
   positiveSign: +1,
   trailingComma: 'in objects', andIn: ['arrays',],
   "backwardsCompatible": "with JSON",
}

example.json5

9. ESLint

ESLintは、バグを回避し、開発チームにコーディング規約を適用する素晴らしいツールです。ECMAScript/JavaScriptコードで見つかったパターンを識別して報告します。

Image for post
ESLint ― https://github.com/eslint/eslint

インストール方法・使い方

$ yarn add eslint

次に、設定ファイルの作成が必要です。

$ ./node_modules/.bin/eslint --init

その後、任意のファイルやディレクトリでESLintを実行できます。

$ ./node_modules/.bin/eslint yourfile.js

詳しくは、公式ドキュメントを確認してください。始め方と設定の例がたくさんあります。

10. PM2

PM2は、Node.jsアプリケーション用のプロダクションプロセスマネージャーで、ロードバランサーを内蔵しています。これにより、アプリケーションを永続化させ、ダウンタイムなしでリロードし、一般的なシステム管理タスクを容易にできます。

Image for post
pm2 ― https://github.com/Unitech/pm2

インストール方法

$ yarn add global pm2

使い方

任意のアプリケーション(Node.js、Python、Ruby、$PATHのバイナリ等)を起動できます。

$ pm2 start app.js

アプリはデーモン化され、監視され、永続化します。プロセス管理の詳細はこちら

アプリケーション管理

起動したアプリケーションを簡単に管理できます。実行中のすべてのアプリケーションを一覧表示する方法は次の通りです。

$ pm2 ls

Image for post
pm2 ls

機能と活用方法の全リストは、公式ドキュメントを確認してください。

11. helmet

helmetライブラリは、さまざまなHTTPヘッダを設定することでExpressアプリを保護するのに役立ちます。「特効薬ではないが、助けにはなる!」

Image for post
helmet ― https://github.com/helmetjs/helmet

インストール方法

yarn add helmet

使い方

helmet はConnectスタイルのミドルウェアで、Expressなどのフレームワークと互換性があります(Koaのサポートが必要な場合は、koa-helmetを確認してください)。

const express = require("express");
const helmet = require("helmet");

const app = express();

app.use(helmet());

トップレベルのhelmetの関数は、11個の小さなミドルウェアのラッパーです。言い換えれば、この2つは同等です。

// This...
app.use(helmet());

// ...is equivalent to this:
app.use(helmet.contentSecurityPolicy());
app.use(helmet.dnsPrefetchControl());
app.use(helmet.expectCt());
app.use(helmet.frameguard());
app.use(helmet.hidePoweredBy());
app.use(helmet.hsts());
app.use(helmet.ieNoOpen());
app.use(helmet.noSniff());
app.use(helmet.permittedCrossDomainPolicies());
app.use(helmet.referrerPolicy());
app.use(helmet.xssFilter());

12. compression

compressionライブラリはNode.jsの圧縮ミドルウェアです。

Image for post
compression ― https://github.com/expressjs/compression

インストール方法

$ yarn add compression

使い方

このモジュールをexpressやconnectで使うには、expressミドルウェアでcompressionを呼び出すだけです。ミドルウェアを通過したリクエストは圧縮されます。

const compression = require('compression')
const express = require('express')

const app = express()

// compress all responses
app.use(compression())

// ...

おわりに

私のニュースレターでは、コンテンツの最新情報の通知を、いち早く受け取ることができます。ぜひ登録してください。

Zack Shapiroに謝意を表します。

翻訳協力

この記事は以下の方々のご協力により公開する事ができました。改めて感謝致します。

Original Author: Indrek Lasn (@lasnindrek)
Original Article: 12 Useful Packages Every Node.js Developer Should Know
Thank you for letting us share your knowledge!

選定担当: @gracen
翻訳担当: @gracen
監査担当: -
公開担当: @gracen

ご意見・ご感想をお待ちしております

今回の記事はいかがでしたか?
・こういう記事が読みたい
・こういうところが良かった
・こうした方が良いのではないか
などなど、率直なご意見を募集しております。
頂いたお声は、今後の記事の質向上に役立たせて頂きますので、お気軽に
コメント欄にてご投稿ください。Twitterでもご意見を受け付けております。
皆様のメッセージをお待ちしております。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

色々めんどくさいのでDjango REST API+React/TypeScriptでアプリ作ってみる1

環境構築

こちらを参考に構築していただければと思います(WIN構築)

キャプチャ.PNG

Djangoの画面が確認できたところから作っていきたいと思います。

はじめに、バックエンドAPIを作成していくためにAPIの設定とフロントへのつなぎ口を記述していきます。

settingの中身を書き換えていきます。

"""
Django settings for xxx_api project.

Generated by 'django-admin startproject' using Django 3.1.

For more information on this file, see
https://docs.djangoproject.com/en/3.1/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.1/ref/settings/
"""

from pathlib import Path
import os
from datetime import timedelta

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'v)yfl4pl$p=y2t+@c3!0*%2w-&m%sp8j-g#g-1o-!n%sl=5s+*'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'corsheaders',   #add
    'rest_framework',   #add
    'api.apps.ApiConfig',  #add
    'djoser',  #add
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',      
    'django.middleware.clickjacking.XFrameOptionsMiddleware', 

CORS_ORIGIN_WHITELIST = [   
    "http://localhost:3000"  #reactの通路を開ける設定
]

ROOT_URLCONF = 'xxx_api.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'xxx_api.wsgi.application'

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ],
}


SIMPLE_JWT = {
    'AUTH_HEADER_TYPES': ('JWT',),
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=30),
}

# Database
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}


# Password validation
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/3.1/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'Asia/Tokyo'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.1/howto/static-files/

STATIC_URL = '/static/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'

INSTALLED_APPS内の説明

djoserを使ったDjango REST FrameworkでのJWT認証機能の実装を行っています
api.apps.ApiConfig = api アプリケーション()

データベース

今回はsqliteを使用しています。

xxx_api/urls.py

"""

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/3.1/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""

from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('api.urls')),
    path('authen/', include('djoser.urls.jwt')),
]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

api/urls.py

from django.urls import path, include
from rest_framework import routers



router = routers.DefaultRouter()


urlpatterns = [
    path('', include(router.urls)),
]



api/model.py


from django.db import models
from django.contrib.auth.models import User
from django.core.validators import MinValueValidator
import uuid


def upload_avatar_path(instance, filename):
    ext = filename.split('.')[-1]
    return '/'.join(['avatars', str(instance.user_profile.id) + str(".") + str(ext)])


class Profile(models.Model):
    user_profile = models.OneToOneField(
        User, related_name='user_profile',
        on_delete=models.CASCADE
    )
    img = models.ImageField(blank=True, null=True, upload_to=upload_avatar_path)

    def __str__(self):
        return self.user_profile.username


class Category(models.Model):
    item = models.CharField(max_length=100)

    def __str__(self):
        return self.item


class Task(models.Model):
    STATUS = (
        ('1', 'Not started'),
        ('2', 'On going'),
        ('3', 'Done'),
    )
    id = models.UUIDField(default=uuid.uuid4, primary_key=True, editable=False)
    task = models.CharField(max_length=100)
    description = models.CharField(max_length=300)
    criteria = models.CharField(max_length=100)
    status = models.CharField(max_length=40, choices=STATUS, default='1')
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    estimate = models.IntegerField(validators=[MinValueValidator(0)])
    owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='owner')
    responsible = models.ForeignKey(User, on_delete=models.CASCADE, related_name='responsible')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.task



C:\Users\xxx\PycharmProjects\xxx_api>python manage.py makemigrations
Migrations for 'api':
  api\migrations\0001_initial.py
    - Create model Category
    - Create model Task
    - Create model Profile

C:\Users\xxx\PycharmProjects\xxx_api>python manage.py migrate
Operations to perform:
  Apply all migrations: admin, api, auth, contenttypes, sessions
Running migrations:
  Applying api.0001_initial... OK

キャプチャ.PNG

キャプチャ.PNG

admin.pyを設定すると

from django.contrib import admin
from .models import Category, Task, Profile

admin.site.register(Category)
admin.site.register(Task)
admin.site.register(Profile)

123.PNG

上記が追加されます。

これで削除・編集・追加・更新が可能となりました

serializers(返値の詳細を決めれるやつ)

1.apiの直下にNEWファイルserializers.pyファイルを作成します。
2.必要なモジュールをimportします
 

serializers.py
from resr_framework import serializers
from .models import Task,Category Profile
from django.contrib.auth.models import User


class UserSerializer(Serializer.ModeSerializer):
   class Meta:
     model = User
     fields = ['id','username','password']
     extra_kwargs = {'password':{'write_only':True, 'required': True}}

def create(self, validated_date):
   user = User.

class UserSerializer(Serializer.ModeSerializer):
   class Meta:
     model = User
     fields = ['id','username','password']
     extra_kwargs = {'password':{'write_only':True, 'required': True}}

def create(self, validated_date):
   user = User.objects.create_user(**validated_date)
   return  user



class Profileserializers(serializers.Modelserializer):
    class Meta:
        model = profile
        fields = ['id', 'user_profile', 'img']
        extra_kwargs = {'user_profile':{'read_only': True}}


class Categoryserializers(serializers.Modelserializer):
      class Meta:
          model = Category
          fields = ['id','item']


class Taskserializers(serializers.Modelserializer):
    category_item = serializers.ReadOnlyField(source='category.item', read_only=True)
    owner_username = serializers.ReadOnlyField(source='owner.username', read_only=True)
    responsible_username = serializers.ReadOnlyField(source='responsible.username', read_only=True)
    status_name = serializers.CharField(source='get_status_display', read_only=True)


    class Meta:
        model = Task


python manage.py migrate でデータベースに情報を落とししていく。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Material-UIで一覧画面を作ってみる(React)

はじめに

今回は、Material-UIの使い方や使ってみた所感を書いていきたいと思います。

Material-UIとは

Material-UIとは、Googleのマテリアルデザインの仕様を取り入れているReact用のUIコンポーネントライブラリです。Material-UIを利用することで手軽に見栄えの良いコンポーネントを作成することができます。

Material-UI

マテリアルデザインとは

マテリアルデザインとは、Googleが提唱している物質的なデザインのガイドラインです。スマートフォンやタブレットなどのタッチデバイスの普及により直感的に操作ができるUIへの需要が高まってきた中で生まれた概念です。

Material Design

環境

今回の使用バージョンはそれぞれ以下の通りです。

  • Node.js v12.19.0
  • React v16.13.1
  • @material-ui/core v4.11.0

Material-UIの導入

次のコマンドを実行し、Material-UIをインストールします。

# npmの場合
npm install @material-ui/core

# yarnの場合
yarn add @material-ui/core

ディレクトリ構成

ディレクトリ構成は以下のようになります。今回はあくまでも簡単なページを作成することを目的としているため、Reactを使用しシステムを作成する場合は、様々な構成を調査し作成したいシステムに合った構成を探してみてください!

Reactのファイル構成について

src/
 ├ components/
 │ └ header.js   
 │ └ card.js     
 ├ pages/
 │ └ _app.js     
 │ └ _document.js     
 │ └ index.js     
 └ public/
 │ └ image/
 │   └ ika.png
 └ package.json

早速使ってみる

今回は公式ドキュメントのソースコードを参考に一覧画面を作成しながら、Material-UIによって作成できるコンポーネントを紹介していきます。

ページの骨組みを用意する

これから作成するコンポーネント達を配置するために、ページの骨組みを用意します。

pages/index.js
import { Header } from '../components/header';
import { Card } from '../components/card';

const Index = () => (
  <div>
    <Header />
    <Card />
  </div>
)

export default Index

表示させてもまだ何もないので真っ白のページが表示されますが、確認方法を説明します。
以下のコマンドでローカルホストを立ち上げ、http://localhost:3000/にアクセスすることで確認できます。

# npmの場合
npm run dev

# yarnの場合
yarn dev

components/header.jsなどインポート宣言しているファイルを作成していないとエラーになるのでご注意ください。

ヘッダーを作成してみる

まずはWebサイトには欠かせないヘッダーを作成していきます。早速書いてみましょう!
実際のコードは以下になります。

components/header.js
export const Header = () => {
  const classes = useStyles();

  return (
    <div className={classes.root}>
      <AppBar position="static">
        <Toolbar>
          <IconButton edge="start" className={classes.menuButton} color="inherit" aria-label="menu">
            <MenuIcon />
          </IconButton>
          <Typography variant="h6" className={classes.title}>
            TOP
          </Typography>
          <Button color="inherit">Login</Button>
        </Toolbar>
      </AppBar> 
    </div>
  );
}

AppbarTypographyなどのタグはインポートをすることで使用できます。
className={classes.root}の記述ではタグにクラス属性を付与し、見た目を調整することができます。

結果は以下になります。シンプルなヘッダーを作成することができました。
image.png

せっかくなのでメニューアイコンを動くようにしてみましょう!以下のように書いていきます。

components/header.js
export const Header = () => {
  const classes = useStyles();
  const theme = useTheme();
  // stateとstateを更新するための関数を宣言
  const [open, setOpen] = React.useState(false);

  // メニューを開く関数
  const handleDrawerOpen = () => {
    setOpen(true);
  };

  // メニューを閉じる関数
  const handleDrawerClose = () => {
    setOpen(false);
  };

  return (
    <div className={classes.root}>
      <CssBaseline />
      <AppBar
        position="fixed"
        className={clsx(classes.appBar, {
          [classes.appBarShift]: open,
        })}
      >
        <Toolbar>
          {/* メニューアイコンクリック時に呼び出す関数を定義 */}
          <IconButton
            color="inherit"
            aria-label="open drawer"
            onClick={handleDrawerOpen}
            edge="start"
            className={clsx(classes.menuButton, open && classes.hide)}
          >
            <MenuIcon />
          </IconButton>
          <Typography variant="h6" noWrap>
            TOP
          </Typography>
        </Toolbar>
      </AppBar>
      {/* メニュー部分 */}
      <Drawer
        className={classes.drawer}
        variant="persistent"
        anchor="left"
        open={open}
        classes={{
          paper: classes.drawerPaper,
        }}
      >
        <div className={classes.drawerHeader}>
          {/* 閉じるアイコンクリック時に呼び出す関数を定義 */}
          <IconButton onClick={handleDrawerClose}>
            {theme.direction === 'ltr' ? <ChevronLeftIcon /> : <ChevronRightIcon />}
          </IconButton>
        </div>
        <Divider />
        <List>
          {links.map((link) => (
            <ListItemLink href={link.link} key={link.id}>
              <ListItemText primary={link.label} />
            </ListItemLink>
          ))}
        </List>
      </Drawer>
      <main
        className={clsx(classes.content, {
          [classes.contentShift]: open,
        })}
      >
      </main>
    </div>
  );
}

先ほどのシンプルなヘッダーに比べて記述量は増えましたが、簡単ですね。
Reactではコンポーネントの状態を管理するためにstateを使用します。stateの更新にはsetStateを使用します。この例ではメニューアイコンをクリックしたときにメニューを開くために、openというstateで状態を管理しています。

無事動かすことができました!

header.gif

コンテンツを作成する

続いてコンテンツを作成します。今回はカードを並べるような感じでコンテンツを一覧表示させますので、カードのコンポーネントを作成していきます。

components/card.js
export const Card = () => {
  const classes = useStyles();

  return (
    <div className={classes.main}>
      <Card className={classes.root}>
        <CardActionArea>
          <CardMedia
            className={classes.media}
            image="イメージパス"
            title="Contemplative Reptile"
          />
          <CardContent>
            <Typography gutterBottom variant="h5" component="h2">
              タイトル
          </Typography>
            <Typography variant="body2" color="textSecondary" component="p">
              説明
          </Typography>
          </CardContent>
        </CardActionArea>
        <CardActions>
          <Button size="small" color="primary">
            Share
        </Button>
          <Button size="small" color="primary">
            Learn More
        </Button>
        </CardActions>
      </Card>
    </div>
  );
}

以下のような一覧画面ができました。(上記のコードは1枚のカードを生成するコードです。)
image.png

レスポンシブ対応について

ちなみに今回作成したページはレスポンシブ対応になっています。スマートフォンで表示したときは以下のような表示になります。驚きなのが、特にレスポンシブ対応のための実装はしていないことです。便利ですね:clap:
SP.gif

今回はMaterial-UIを使用し、このような一覧画面を作成することができました。簡単に見た目の良いページを作成でき扱いやすさを感じました!

あとがき

Webシステムのクオリティを上げるには処理の精度を上げることと並行してデザインも重要ですよね。私自身、最近の業務でデザイナーさんの力を全力で感じています。
ただ、システム作成の際に見栄えを良くしたいと思ってもデザインに割く時間が無かったり、デザイナーさんに頼めないという場面もあるかと思います。そんな時は今回紹介したMaterial-UIなどのUIフレームワークを利用してみるのもいいかもしれません。Material-UI以外にも多種多様なUIフレームワークが用意されていますので気になった方はぜひ一度使ってみてください。

最後までお読みいただき、ありがとうございました:slight_smile::sunflower:

参考資料

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

M1 MacでLIFFとReactを使う際の環境構築

初めに

2021年3月の現在、Open Hack U 2020 Online Vol.4に挑戦しています。
チームの制作物で、LIFF (LINE Front-end Framework)を使ったものを作成することにしました。
M1 Macbook AirでLIFFを使うのは、初めてです。
そこで、環境構築などで詰まったところを書いておき、再度M1 MacでLIFFを使うときにつまらないようにするための備忘録です。

本記事の目的

本記事は、ハッカソンなどで、M1 Macbookを使う人のために書きました。
色々なアプリを作る時の候補として、LIFFを使ったアプリ開発に挑戦してみては、いかがでしょうか?

詰まったところ

参考記事1を進めていたところ、yarnがうまく使えないことがわかりました。
そのため、現時点でのM1チップのMacでは、yarnを使うのをやめましょう。

最終的なコード

最終的に作成したアプリは、こちらです。
メッセージを送信し、GoogleをChrome(デフォルトに設定してあるアプリ)で開き、友達に
メッセージを送ることができるようにしています。

進める中でインストールしたアプリ

https://dashboard.ngrok.com/get-started/setup
デプロイ先

作成したアプリ

+IMG_2395.gif

参考記事

  1. React + TypeScriptを使ってLIFFを作る
  2. 【M1 Mac】React開発環境の構築
  3. nodebrewの「Warning: Failed to create the file」への対処
  4. npmとyarnのコマンド早見表
  5. MacにngrokをインストールしてローカルWebサーバを外から接続できるようにする
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React 入門 (#1) ~DOM操作とReactライブラリ導入~

この記事では、Reactの基礎について学んでいきます。

2021-03-14_12h56_17.png

ソースコードブラウザ表示を中心に進めていきます。
ソースコードには、そのコードが何を表しているのかを話し口調(下の赤枠)で書いているので
そこに注目して読むとより理解が深まると思います。

例)
2021-03-14_13h23_51.png

楽しんで学習をすすめていきましょう。

1,JavaScriptを記述してDOMの操作を行ってみます。

基本的なHTMLのソースコードです。
code.png

index.htmlにリンクしているindex.js(JavaScript)です。
code2.png

2,実際に上のソースコードの動きを確認してみましょう。

2021-03-14_12h37_47.gif

とてもシンプルな動きですね。
これが、JavaScript によるDOM操作になります。

3,Reactライブラリの導入

書き換えたindex.htmlのソースコード
code4.png

上のサイトから、追記したソースコードをコピーしてください。
もしくは、こちらをコピーしてください。

index.html
  //追記 ReactライブラリとReactDOMライブラリ
<script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>

Reactを使うには、
ReactライブラリReactDOMライブラリを使います。
これで、準備完了です。

index.jsファイルに移動して次のコードを記述します。
code5.png

4,ブラウザ表示

index.htmlにはdivタグしかないですが、ちゃんと表示されていますね。
2021-03-14_13h43_54.png

次回は、

今回は、Reactの機能の部分を簡単に紹介しました。
次回は、Reactを学ぶうえで大事なBabelJSXに触れたいと思います。
リンク

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

material-ui DataGridのpaginationラベルをローカライズしたい

DataGridのテキストローカライズ

参考:DataGrid API

DataGrid用のローカライズpropはlocaleTextにオブジェクト入れればできるんですが、デフォルトのソースを見る限りではフッターのパジネーション部分のキーがありませんでした。

TablePaginationが利用されている

DataGrid内部ではmaterial-uiのcoreで提供されているTablePaginationを使っているみたいでした。

参考:GridPagination.tsx

なので、単純にcreateMuiThemeからデフォルトpropを変更すれば良さそうです。

labelDisplayedRows propの上書き

createMuiTheme > props > MuiTablePagination > labelDisplayedRowsにカスタマイズしたいように置くだけです。

参考:TablePagination API

const theme = createMuiTheme({
  props: {
    MuiTablePagination: {
      labelDisplayedRows: ({
        from,
        to,
        count,
      }: LabelDisplayedRowsArgs) => {
        return `${from}-${to} / ${count !== -1 ? count : `${to}以上`}`;
      },
    },
  },
});
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel8 ReactHooks & MySQL Create処理

やりたいこと

フロントはReactHooksを使用して、LaravelはAPIサーバーでバックエンド処理をおこなう。
バックエンド処理はaxiosを使用。

完成図

e147bdcac8ccf1b01931c53e89227bdd.png

開発環境

  • Laravel 8.0
  • php 7.3
  • MySQL 8

State

  • const [ name, setName ] = useState(null);

入力値name(名前)のState

  • const [ email, setEmail ] = useState(null);

入力値email(メールアドレス)のState

  • const [ city, setCity ] = useState(null);

入力値city(住所)のState

  • const [ address, setAddress ] = useState(null);

入力値address(市区町村)のState

  • const [ phone, setPhone ] = useState(null);

入力値phone(電話番号)のState

入力フォーム

map処理はkeyを明示する必要があるので注意

keyの明示についてはVirtualDOMのdiffから実際のDOMに反映させるときに最小限の変更にするために使われる。

form
import React, { useEffect, useState  } from 'react';

import employeeServices from "../services/Employee"

function Form(){

  const [ name, setName ] = useState(null);
  const [ email, setEmail ] = useState(null);
  const [ city, setCity ] = useState(null);
  const [ address, setAddress ] = useState(null);
  const [ phone, setPhone ] = useState(null);
  const [ rol, setRol ] = useState(null);
  const [ listRol, setListRol ] = useState([]);

  useEffect(() => {
    async function fetchDataRol() {
      // load data from API
      const res = await employeeServices.list();
      setListRol(res.data)
    }
    fetchDataRol();
  },[]);

  const saveEmployee = async () => {

    const data = {
      name, email, city, address, phone, rol
    }
    const res = await employeeServices.save(data);

    if (res.success) {
      alert(res.message)
    }
    else {
      alert(res.message)
    }
  }

  return(
    <div>
      <div className="row">
        <div className="col-md-6 mb-3">
          <label htmlFor="firstName">Name employee </label>
          <input type="text" className="form-control" placeholder="Name"
            onChange={(event)=>setName(event.target.value)} />
        </div>
      </div>

      <div className="row">
        <div className="col-md-6 mb-3">
          <label htmlFor="email">Email</label>
          <input type="email" className="form-control" placeholder="you@example.com"
            onChange={(event)=>setEmail(event.target.value)} />
        </div>
      </div>

      <div className="row">
        <div className="col-md-6 mb-3">
          <label htmlFor="phone">City </label>
          <select id="inputState" className="form-control" onChange={(event)=> setCity(event.target.value)}>
             <option selected>Choose...</option>
             <option value="London">London</option>
             <option value="Madrid">Madrid</option>
             <option value="New York">New York</option>
          </select>
        </div>
      </div>

      <div className="row">
        <div className="col-md-6 mb-3">
          <label htmlFor="address">Address</label>
          <input type="text" className="form-control" placeholder="1234 Main St"
            onChange={(event)=>setAddress(event.target.value)} />
        </div>
      </div>

      <div className="row">
        <div className="col-md-6 mb-3">
          <label htmlFor="phone">Phone </label>
          <input type="text" className="form-control" placeholder="123467890"
              onChange={(event)=>setPhone(event.target.value)}  />
        </div>
      </div>

      <div className="row">
        <div className="col-md-6 mb-3">
                    <label htmlFor="phone">Rol </label>
                    <select id="inputState" className="form-control" onChange={(event)=> setRol(event.target.value)}>
             <option selected>Choose...</option>
             {
               listRol.map((item)=>{
                 return(
                   <option key={item.rol_id} value={item.rol_id}>{item.rol_name}</option>
                 )
               })
             }
          </select>
        </div>
      </div>

      <div className="row">
        <div className="col-md-6 mb-3">
          <button className="btn btn-primary btn-block" type="submit"
          onClick={()=>saveEmployee()}>Save</button>
        </div>
      </div>
    </div>
  )
}

export default Form;

saveEmployee押化後、axiosのemployee.saveが呼び出されてlocalhost:8000/api/employee/createにPOSTする。

axios通信

axios
const baseUrl = "http://localhost:8000/api/employee"
import axios from "axios";
const employee = {};

employee.save = async (data) => {
  const urlSave= baseUrl+"/create"
  const res = await axios.post(urlSave,data)
  .then(response=> {return response.data })
  .catch(error=>{ return error; })
  return res;
}

export default employee
api.php
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

Route::middleware('auth:api')->get('/user', function (Request $request) {
    return $request->user();
});

Route::post('/employee/create', 'App\Http\Controllers\API\EmployeeController@create');

Laravel側のControllerが呼び出され、実際に処理をするcreateアクションを呼び出します。

保存処理の最後にはresponseを返してあげる。今回は動作確認のため、失敗時にエラーメッセージのみを返しています。

コントローラー

EmployeeController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Employee;

class EmployeeController extends Controller
{
    public function index(){
        return view("employee");
      }

      public function create(Request $request){

        try {

          $insert['name_lastname'] = $request['name'];
          $insert['email'] = $request['email'];
          $insert['city'] = $request['city'];
          $insert['direction'] = $request['address'];
          $insert['phone'] = $request['phone'];

          Employee::insert($insert);

          $response['message'] = "Save succesful";
          $response['succes'] = true;

        } catch (\Exception $e) {
          $response['message'] = $e->getMessage();
          $response['error'] = true;
        }

        return $response;
      }
}

createアクションはこのような流れで実装できます

指摘などがございましたらコメントで教えていただけると幸いです^_^

おわり

次はレコードをmapで繰り返し表示する方法など記事にしていこうと思います〜!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel8+ReactHooks+MySQL Create処理

やりたいこと

フロントはReactHooksを使用して、LaravelはAPIサーバーでバックエンド処理をおこなう。
バックエンド処理はaxiosを使用。

開発環境

  • Laravel 8.0
  • php 7.3
  • MySQL 8

ModelはItem カラムはtext/commentと仮定しています。

State

  • const [ text, setText ] = useState("");

入力値textのState

  • const [ comment, setComment] = useState("");

入力値commentのState

入力フォーム

入力値をSetStatで更新。submitでsaveEmployeeを呼び出す。

form
return (
      <form className="add-form">
        <div className="add-text">
          <label>内容</label>
          <input type="text" value={text} onChange={(event)=>setText(event.target.value)}/>
        </div>
        <div className="add-comment">
          <label>コメント</label>
          <input type="number" value={amount} onChange={(event)=>setComment(event.target.value)}}/>
        </div>

        <div className="form-row">
        <color="primary" aria-label="add"
          type="submit"
          onClick={()=>saveEmployee()}>追加</div>
    </form> 
    )

呼び出されたsaveEmployeeはdataに定義された値を保持してemployeeServices.saveにアクセス。
この際、dataに定義されているtextとcommentはuseStateを参照しています。

axios
const saveEmployee = async () => {

const data = {
  text, 
  comment, 
};

  const res = await employeeServices.save(data);
  setItemlist(res.data)

}

saveEmployeeから渡された値(data)を引数に、LaravelのAPIサーバーにPOSTする。

employee
let url = location.href;

const baseUrl = url + 'api/employee';

const employee = {};

employee.save = async (data) => {
  const urlSave = baseUrl+"/save"
  const res = await axios.post(urlSave,data)
  .then(response=>{ return response.data; })
  .catch(error=>{ return error; })
  return res;
}

Laravel側のapi.phpが呼び出され、実際に処理をするcreateアクションを呼び出します。

api.php
use App\Http\Controllers\EmployeeController;

Route::match(['get', 'post'],'/employee/itemcreate', [EmployeeController::class, 'itemcreate']);

itemcreateメソッド内で保存処理を行い、responseに$itemsを渡してreturnしてあげる。

ItemController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Item;

class EmployeeController extends Controller
{
      public function itemcreate(Request $request){

        try {
            $items = new Item();
            $items->text = $request->text;
            $items->amount = $request->comment;
            $items->save();

            $response['data'] = $items;
            $response['message'] = '成功';
            $response['success'] = true;

          } catch (\Exception $e) {
            $response['message'] = $e->getMessage();
            $response['success'] = false;
          }
          return $response;
      }

}

createアクションはこのような流れで実装できます

指摘などがございましたらコメントで教えていただけると幸いです^_^

次はレコードをmapで繰り返し表示する方法など記事にしていこうと思います〜!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む