20191127のReactに関する記事は6件です。

RailsアプリのViewをReactに変えた後、デプロイする手順

この記事は?

Railsサーバーと通信するReactアプリをデプロイする時の、自分が踏んだ手順を紹介します。
以前作成したRailsアプリのViewをReactで作り直し、最低限の機能実装まではできたのでデプロイしようとしたら手間取りました。

手順

以下、自分が踏んだ手順を紹介します。

git pull

Jenkinsとか使ってる人はいらないでしょう。

npm install -g npm

そのままnpm installしようとしたら、先にやるように警告された。
そのまま打ったら「permission denied」エラーを出したのでsudoをつけた

npm install

create react appで使用するパッケージを読み込む
終わったらnode_modulesディレクトリが増えていることを確認します。

npm run build

Reactをこれでビルドします。

が、ここで問題が発生

$ npm run build

> client@0.1.0 build /var/www/rails/foreign_books_search/client
> react-scripts build

Creating an optimized production build...
Failed to compile.

./src/App.js
Cannot find file './css/App.css' in './src'.

確認してみると、そもそも./src/css/ディレクトリが存在しない。

よく考えたら、自分はCSSではなくSCSSを使用しているので、Reactをビルドする前にまずSCSSをビルドしなければならない。

というわけで、自分が作成したgulpfile.js

'use strict';

var gulp = require('gulp');
var sass = require('gulp-sass');

sass.compiler = require('node-sass');

gulp.task('default', function () {
  return gulp.src('./src/**/*.scss')
    .pipe(sass().on('error', sass.logError))
    .pipe(gulp.dest('./src/css'));
});

gulp.task('sass:watch', function () {
  gulp.watch('./sass/**/*.scss', ['sass']);
});

なので、以下のコマンド

$ gulp
-bash: gulp: コマンドが見つかりません

...?

node_modulesにPATHが通ってない!

$ export PATH=$PATH:node_modules/.bin
$ gulp -v
CLI version: 2.2.0
Local version: 4.0.2
$ gulp
[23:02:47] Using gulpfile /var/www/rails/my_app/client/gulpfile.js
[23:02:47] Starting 'default'...
[23:02:47] Finished 'default' after 53 ms

./src/css/があることを確認したので、改めてReactのビルドを行います。

$ npm run build

> client@0.1.0 build /var/www/rails/foreign_books_search/client
> react-scripts build

Creating an optimized production build...
Failed to compile.

./src/App.js
Cannot find module: '@material-ui/core'. Make sure this package is installed.

You can install this package by running: yarn add @material-ui/core.

今度はmaterial-uiが無いと。。。

指示に従いインストール

$ yarn add @material-ui/core
yarn add v1.15.2

※色々表示される

Done in 92.95s.

3度めの正直を祈って!

$ npm run build

> client@0.1.0 build /var/www/rails/foreign_books_search/client
> react-scripts build

Creating an optimized production build...
Compiled successfully.

File sizes after gzip:

  92.81 KB  build/static/js/2.f13f31dd.chunk.js
  3.75 KB   build/static/js/main.29ffb305.chunk.js
  772 B     build/static/js/runtime-main.b240f91f.js
  681 B     build/static/css/main.9ea7ac83.chunk.css
  580 B     build/static/css/2.94155858.chunk.css

The project was built assuming it is hosted at the server root.
You can control this with the homepage field in your package.json.
For example, add this to build it for GitHub Pages:

  "homepage" : "http://myname.github.io/myapp",

The build folder is ready to be deployed.
You may serve it with a static server:

  yarn global add serve
  serve -s build

Find out more about deployment here:

  https://bit.ly/CRA-deploy

Compiled successfully !

build/ディレクトリにindex.htmlがあることも確認しました。

Nginxの設定

設定ファイルのrootbuild/ディレクトリにする

設定の読み込み

$ sudo systemctl reload nginx

その後、ドメインにアクセスし、アプリが表示されることを確認!

ついでに、今後新しい修正を反映するときはnpm run buildやunicornの再起動を忘れずに。

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

仮想DOMの時代にDOMを返すAPIを扱う(CSR編)

DMMグループ Advent Calendar 2019 12日目の記事です。

アドカレ映えしない、地味~な内容ですが、よろしくお願いします。

TL;DR

Angular / React / Vue製サイトにWebAPI経由で返されたDOMを挿入する

Angular React Vue
image.png image.png image.png

前書き

大規模Webサイトの各ページへ横断的に要素を挿入したい要件があるとします。

  • ブランドロゴ、ナビゲーション、トラッキングタグなど
  • 大規模なので、ページ間で管轄が分かれていたり、別々のアーキテクチャで構築されていたりする
  • 要素の管轄は一部署に集約したい

これらの要件を満たす手段として、 DOM Stringを返すようなAPIが提供される ことがあります。

他の手段としては「DOMを構築・挿入するスクリプトを配布する」という方法もありますが、それと比べて以下のようなメリットが期待できます。

  • 呼び出しタイミングや組み込み位置をある程度制御できる
  • サーバサイド・クライアントサイドのどちらからでも呼び出せる

「クライアントサイドからでも呼び出せる」とはいえ、必ずしもすんなりできるとは限らず、特に昨今のJSフレームワークで開発されたWebサービスに組み込むにはハマりどころが色々あります。

今回はAngular, React, Vue製でクライアントサイドレンダリングするようなWebサイトにそのようなAPIを組み込む際の方法や注意を纏めています。

[全般]DomStringをDOMとしてrenderされるようにinnerHtmlラッパを使う

いずれのフレームワークもXSS対策のため、テンプレートやJSX中で使用される変数はHTMLエスケープされるようになっています。

そのため、APIレスポンスのDOM stringをそのままテンプレートやJSXへ書き出そうとしても、HTMLがプレーンテキストとして表示されるだけでHTML要素としては読み込まれません。

そこで、変数中のHTMLをHTML要素として表示するための機能が提供されておりますので、そちらを利用します。

大前提としてAPI側のXSS対策はなされているものとします。

Angular React Vue
innerHTML dangerouslySetInnerHTML v-html

[linkタグ]Angularの場合はlinkタグの扱いに注意

APIレスポンスの中に、スタイルシートを読み込むためのlinkタグが含まれる場合の話です。

React, Vueは上記の方法でlinkタグを出力することが出来るのですが、AngularではinnerHTMLでHTML要素を出力する際に、やはりXSS対策のためlinkタグやscriptタグが削除されるようになっています。

これを回避する方法も提供されており、DomSanitizerのbypassSecurityTrustHtmlメソッドを利用することでlinkタグを出力することができます。

[scriptタグ]innerHTMLではJSを実行出来ない

APIレスポンスにscriptタグが含まれる場合は厄介です。

innerHTML(やこれまで挙げたFWのinnerHTMLラッパ)を利用することで、DOMやスタイルを反映させることができますが、scriptタグ経由のJSを呼び出すことは出来ません。

これはW3CのHTML5 scriptタグの仕様として記述されています。

When inserted using the document.write() method, script elements execute (typically synchronously), but when inserted using innerHTML and outerHTML attributes, they do not execute at all.

これについては、フレームワーク側のサポートはなく、動的にJSを実行する方法などに記載されているように、

  1. document.createElement('script'); でscript要素を作成、
  2. domStringをパースしてscript要素を組み立てていき、
  3. 最終的に document.body.appendChild などで完成したscript要素を挿入する

という、骨の折れる作業をする必要がありそうです。

[実践]ヘッダー・フッターでコンテンツを挟み込む

ここで少し具体的な話になります。

APIのレスポンスとして、複数のUI、例えばヘッダーとフッターが一遍に返されるような場合はどのように実装すればよいでしょうか。

image.png

この場合、ヘッダー・フッターを1つのコンポーネントとして扱うのが良さそうです。

各フレームワークとも、似たようなかたちで任意のコンテンツを挟み込むようなコンポーネントを実装出来ます。

Angular React Vue
ng-content props.children slot

実装例 / Angular

app.component.html
<navigation>
  <!-- サイトコンテンツ -->
</navigation>
navigation.component.html
<div [innerHTML]='header'></div>
<ng-content></ng-content>
<div [innerHTML]='footer'></div>

実装例 / React

App.js(内のJSX)
<Navigation>
  <!-- サイトコンテンツ -->
</Navigation>
Navigation.js(内のJSX)
<div dangerouslySetInnerHTML={{ __html: header }} />
  {this.props.children}
<div dangerouslySetInnerHTML={{ __html: footer }} />

実装例 / Vue

index.html
<navigation>
  <!-- サイトコンテンツ -->
</navigation>
template
<div v-html="header"></div>
  <slot></slot>
<div v-html="footer"></div>

おわり

  • 本記事はDOMを返すようなAPIをmanageする、というのがテーマであり、共通UIを提供する手段としてDOMを返すAPIの設計を推奨するものではありません。
  • アプリケーションをSSRする場合は更なる課題がありそうですので、それはまた次の機会に。
  • 明日は @uruha さんより、Virtual DOMの時代の楽しい話が聞けるんじゃないかと思うので、乞うご期待!

サンプルアプリの元ネタ

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

Github PagesでReactアプリケーションをデプロイ

はじめに

久しぶりのQiitaの記事投稿です!
今回はGithub PagesにReactアプリケーションのデプロイ方法を記述しました。
途中つまづいた点があったので、付録として記述しました。
また、今回は環境によって

[Step1] Reactアプリケーションの作成

必要なもの

  • node (バージョン 8.10以上)
  • npm (バージョン 5.6以上)
  • Githubアカウント (アカウントが無い方はこちらから作成)

Reactアプリケーションの作成

$ npx create-react-app {アプリケーション名}

[Step2] gh-pagesパッケージのインストール

プロジェクトディレクトリに移動

$ cd {アプリケーション名}

gh-pagesのインストール

$ yarn add --dev gh-pages
or
$ npm install --save-dev gh-pages

[Step3] package.jsonの変更

predeployは自分が使用したパッケージ管理ツールによって変わります。
npm => npm run build
yarn => yarn run build

{
  // ...
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    // 追加
    "predeploy": "{yarn or npm} run build",
    "deploy": "gh-pages -d build"
  },
  // ...
  "devDependencies": {
    "gh-pages": "^2.1.1"
  },
  // 追加
  "homepage": "https://{Githubのユーザ名}.github.io/{リポジトリ名}/"
}

[Step4] Githubにリポジトリの作成

  1. githubで新しいリポジトリ作成
  2. アプリケーションをデプロイ

自分のパッケージ管理ツールによって実行コマンドが異なります。

$ git init
$ git remote add origin https://github.com/{githubアカウント名}/{リポジトリ名}.git
$ {yarn or npm} run deploy 

[Step5] アプリケーションの確認

deployできたら
https://{githubアカウント名}.github.io/{リポジトリ名}/
にアクセスするとアプリケーションが表示されると思います。

スクリーンショット 2019-11-27 16.50.45.png

付録

[Step4]のdeployでエラーが起きた場合!

...
$ gh-pages -d build
Cloning into 'node_modules/gh-pages/.cache/git@github!M01tyan!credit-card-form.git'...
ssh: Could not resolve hostname github: nodename nor servname provided, or not known
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

deployに失敗して、上のようなエラーメッセージが表示された場合は
下記のコマンドを入力して、リモートリポジトリのurlを変更する必要があります。

$ git remote set-url origin git@github.com:{githubアカウント名}/{リポジトリ名}.git

変更したら再び {yarn or npm} run deploy を実行

Published
と表示されたら成功!!

参考記事

https://qiita.com/yuitnnn/items/11375ea29ec023d19fdf
https://qiita.com/EisKern/items/15dcf7864fa49df8f247
https://dev.to/yuribenjamin/how-to-deploy-react-app-in-github-pages-2a1f

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

React + TypeScript でTodoリストを作りながら入門

昔Vueを勉強する前にチラっとReactをやったものの挫折して、それ以来触って無かった者です。

最近Reactは分かりやすくなってるとか、TypeScriptとの相性が良いとか聞いたので少しやってみることにしました。

今回は色々な資料を当たりながら最終的にシンプルなTodoリストを作成しました。

React用の型が追加される「@types/react」も使ってみます。
何か間違ってたら指摘お願いします。

:bee: 作るもの

new - CodeSandbox.png
Codesandbox

追加と削除だけのとてもシンプルなTODOリスト。

:tools: コードを見てみる

Todoの型を決める

import * as React from "react";
import * as ReactDOM from "react-dom";

// ***************************************
// Todoの型定義
// ***************************************
interface Todo {
  id: number;
  name: string;
}

Todoは、idとnameというプロパティを持つ型だと決めている。
idはnumber型であり、nameはstring型である。

Todoコンポーネントを作る

Todoリスト1つづつの部分である。
HTMLタグで言うと ”li” の部分に当たる。

// ***************************************
// Todoコンポーネント
// ***************************************
// TodoListItemPropsの型定義
interface TodoListItemProps {
  todo: Todo;
  onDelete: (todo: Todo) => void;
}

const TodoListItem: React.FunctionComponent<TodoListItemProps> = ({
  todo,
  onDelete
}) => {
  const onClick = () => {
    onDelete(todo);
  };

  return (
    <li>
      {todo.name} <button onClick={onClick}>Delete</button>
    </li>
  );
};

TodoListItemコンポーネントは、「React.FunctionComponent」という型に従いそのPropsは「TodoListItemProps」という型に従うと決める。

「FunctionComponent」は自分では定義していない。
「@types/react」が提供してくれる型。

interface FunctionComponent<P = {}> {
    (props: PropsWithChildren<P>, context?: any): ReactElement | null;
    propTypes?: WeakValidationMap<P>;
    contextTypes?: ValidationMap<any>;
    defaultProps?: Partial<P>;
    displayName?: string;
}

Macなら「Option + クリック」、winなら「ctrl + クリック」で定義されている型のところへ飛べるので、見てみるとこんな感じらしい。

うん、よく分からんけど関数のコンポーネントなんやろ(適当)

Todoリストコンポーネントを作る

フォームを含まないTodoリスト全体の部分。
HTMLタグで言うと ”ul” の部分に当たる。

上記の「TodoListItem」の親になる。

// ***************************************
// Todoリストの一覧表示部分
// ***************************************
// Propsの型定義
interface TodosListProps {
  todos: Todo[];
  onDelete: (todo: Todo) => void;
}

const TodosList: React.FunctionComponent<TodosListProps> = ({
  todos,
  onDelete
}) => (
  <ul>
    {todos.map(todo => (
      <TodoListItem todo={todo} key={todo.id} onDelete={onDelete} />
    ))}
  </ul>
);

フォーム部分のコンポーネントを作る

ユーザーが入力するフォームや、追加ボタンの部分。

// ***************************************
// Todo追加用フォーム
// ***************************************
// Propsの型定義
interface NewTodoFormProps {
  onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
  onAdd: (event: React.FormEvent<HTMLFormElement>) => void;
  todo: Todo;
}

const NewTodoForm: React.FunctionComponent<NewTodoFormProps> = ({
  onChange,
  onAdd,
  todo
}) => (
  <form onSubmit={onAdd}>
    <input onChange={onChange} value={todo.name} />
    <button type="submit">Add a todo</button>
  </form>
);

特筆するところはないけど、「React.ChangeEvent」「React.FormEvent」なども上記のように「Option + クリック」「ctrl + クリック」でどういう型なので見れるので興味があれば見てみると良いかもしれない。

Todoアプリ全体のコンポーネント

一番親の部分

// ***************************************
// Todoリスト本体部分
// ***************************************
// 状態(State)の型定義
interface State {
  newTodo: Todo;
  todos: Todo[];
}

class App extends React.Component<{}, State> {
  state = {
    newTodo: {
      id: 1,
      name: ""
    },
    todos: []
  };

  render() {
    return (
      <div>
        <h2>React + TypeScript Todoリスト</h2>
        <NewTodoForm
          todo={this.state.newTodo}
          onAdd={this.addTodo}
          onChange={this.handleTodoChange}
        />
        <TodosList todos={this.state.todos} onDelete={this.deleteTodo} />
      </div>
    );
  }

  // Todoの追加
  private addTodo = (event: React.FormEvent<HTMLFormElement>) => {
    // デフォルトの送信機能を使わない
    event.preventDefault();

    // 空なら処理を止める
    if (!this.state.newTodo.name.length) {
      return;
    }

    // TodoリストStateの更新、引数には現在(更新前)のStateが入ってくる
    this.setState(previousState => ({
      newTodo: {
        id: previousState.newTodo.id + 1,
        name: ""
      },
      // 現在のTODOの配列と、新しいTODOを結合する
      todos: [...previousState.todos, previousState.newTodo]
    }));
  };

  // フォームの内容が変わったらnewTodoの内容も変える
  private handleTodoChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const inputedValue = event.target.value;

    this.setState({
      newTodo: {
        ...this.state.newTodo,
        name: inputedValue
      }
    });
  };

  // Todoの削除
  private deleteTodo = (todoToDelete: Todo) => {
    this.setState(previousState => ({
      todos: [
        ...previousState.todos.filter(todo => todo.id !== todoToDelete.id)
      ]
    }));
  };
}

ReactDOM.render(<App />, document.getElementById("root"));

:star: まとめ

何だかReactは凄く難しいイメージがあったのですが、意外とシンプルで基本普通のJavaScriptに近い形で書けるのが良いと思いました。

またTypeScriptも趣味でチョロっとしか触ったこと無かったのですが、補完やコードジャンプやエラー通知が便利だなと実感出来ました!

ここ数ヶ月Swiftを勉強していたのですが、結構TypeScriptと似ている印象です。

以上

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

CSS in JS時代のCSS設計

Ateam Lifestyle Advent Calendar 2019の1日目は
株式会社エイチームライフスタイル クリエイティブ戦略部 シニアデザイナーの綿貫が担当します!

先陣切って投稿しますので、みなさんどうぞ読んでください!

はじめに

CSS in JSもそこまで珍しくなくなってきたと思いますが、CSS in JSでのCSS設計の話はなかなか見かけません。
ないなら自分で書こう!と思い立ったので書きます。

そもそもCSS設計が必要な理由

CSSを書くに当たって、人間が悩むのは主に「名前空間が存在していない」からだと思います。
1つ1つのパーツ個別にスタイリングをするだけならさほど難しくもありませんよね?

ところが、サイト全体を通してスタイリングするとなると

  • 自分が触っている箇所に
    • 予期せぬスタイルが当たっている
    • 当たるはずのスタイルが当たらない
  • 自分が触っていない箇所に
    • 予期せぬスタイルが当たっている

などに悩まされるかと思います。

大抵の場合はカスケーディングセレクタの詳細度が絡み合い事故を起こすのですが、名前空間を持っていれば防げたであろうケースも多いのです。

こういったつらさを解消するためにCSS設計が必要であり、色々な種類のものが考案されてきました。
この記事では詳しくは触れませんが、一例を挙げますので気になる方は読んでみてください。

CSS in JSについて

ここまで散々CSS in JSという言葉を使ってきましたが、これはある特定の技術を指すものではありません。
言葉通り「JavaScriptの中でCSSを書く」という概念です。

実際にコードを書く際にはライブラリを選ぶところから始まります。

有名どころだと以下のもの。

CSS Modulesは学習コストが一番低く、styled componentsは普及している範囲が一番広く、Emotionは最近追い上げて来ている&個人的に好きです。

どれも共通しているのは、擬似的にローカルスコープを生成してセレクタを閉じ込められる点。
これが出来るようになったおかげで命名に悩む時間は大幅に減りました。

では名前空間の問題が解消した上で、それでもなお、人間が設計しないといけないことは何でしょうか?

CSS in JSでの設計とこれまでの設計との違い

大きく分類すれば

  • グローバルに使うものとそうでないものを明らかにする
  • propsによるスタイルの変化を考慮する
  • どの機能・オプションを使うのかを明示する

ことに分かれると思います。

グローバルに使うものとそうでないものを明らかにする

UIをコンポーネントとして実装する際のメリットはそれぞれが独立していることです。
スタイリングにおいても大きなメリットですが、そうは言ってもグローバルに共通化したいものもあります。

代表例はカラーパレット。
特にブランドカラーを定義している際は、毎回手打ちで色を指定するのは非現実的です。

あるいはreset CSSの類。
各コンポーネントにいちいち記述するのは絶対に嫌です。

かといってなんでもかんでも共通化していては、せっかくのローカルスコープが意味をなさなくなってしまうのが悩みどころ。
チームやプロダクトによりますが、どこまでを共通化してどこからは各コンポーネントに閉じ込めるかをしっかり話し合うのが大事だと思います。

範囲さえ決めることが出来れば、先に挙げたライブラリならどれでもglobalなスタイルも定義できます。
また、定数や関数を他で定義しておいて読み込めばいつでも使いまわせます。

propsによるスタイルの変化を考慮する

CSS in JSで楽になることの1つとして、propsを渡してスタイルを変更できることが挙げられます。
選ぶライブラリによって実装方法は変わりますが、例えばstyled componentsなら以下のようにバリエーションを生成できます。

  1. InputコンポーネントにinputColorが指定されていたらcolorがその値になる
  2. 指定されていなかったらフォールバックとしてpalevioletredが適用される
// 公式ドキュメントよりコードを抜粋
const Input = styled.input`
  padding: 0.5em;
  margin: 0.5em;
  color: ${props => props.inputColor || "palevioletred"};
  background: papayawhip;
  border: none;
  border-radius: 3px;
`;

render(
  <div>
    <Input defaultValue="@probablyup" type="text" />
    <Input defaultValue="@geelen" type="text" inputColor="rebeccapurple" />
  </div>
);

しかし、なんでもかんでもpropsで渡せるようにしてしまっては記述が汚くなります。
やろうと思えば……

// 先ほどのコードを改変
const Input = styled.input`
  padding: ${props => props.inputPadding || 0.5}em;
  margin: ${props => props.inputMargin || 0.5}em;
  color: ${props => props.inputColor || "palevioletred"};
  background: ${props => props.inputBackground || "papayawhip"};
  border: none;
  border-radius: ${props => props.inputBorderRadius || 3}px;
`;

render(
  <div>
    <Input defaultValue="@probablyup" type="text" />
    <Input
      defaultValue="@geelen"
      type="text"
      inputPadding="2"
      inputMargin="1"
      inputColor="rebeccapurple"
      inputBackground="red"
      inputBorderRadius="5"
    />
  </div>
);

かなり嫌なコードですよね?
全てのカスタムを許せば良いってものじゃありませんし、Inputはもはやコンポーネントとして成立しているのか怪しいです。

こういった場合は以下のような書き方にすることもできます。

// 更に改変
const Input = styled.input`
  padding: 0.5em;
  margin: 0.5em;
  color: palevioletred;
  background: papayawhip;
  border: none;
  border-radius: 3px;
  ${props => props.primary && css`
    padding: 1em;
    margin: 1em;
    color: rebeccapurple;
    background: red;
    border-radius: 5px;
  `}
`;

render(
  <div>
    <Input defaultValue="@probablyup" type="text" />
    <Input defaultValue="@geelen" type="text" primary />
  </div>
);

これなら自由すぎるカスタム性はなくなり、Inputの書き方もスッキリしました。

ただし、これだけ変わるなら別コンポーネントとして定義した方が良いのでは?とか、
今後secondarytertiaryが出てきたらどうなるのか?とか
propsの渡し方はboolとenumのどちらの方が良いのか?とか
疑問が次々湧きますよね。

色々なやり方でpropsをの受け渡しとスタイルの変更ができてしまうため、何を許容して何を縛るかの設計が大事になるかと思います。

どの機能・オプションを使うのかを明示する

上の話とも少し似ています。
今CSS in JSのライブラリはお互いがしのぎを削っているからか、各種オプションが非常に豊富です。

  • オブジェクト記法でも書けるし、テンプレートリテラルでも書ける
  • component化もできるし、インラインで直接も書ける
  • jsx内にも書けるし、外にconstとしても書ける

などなど……。
アレコレできるようになった反面、縛りは入れないと非常に読みづらくなります。

例えばEmotionは最後発なのでかなり色々な書き方ができるのですが、以下を見てください。

/** @jsx jsx */
import { css, jsx } from '@emotion/core'

const text = css`
  font-size: 20px;
  padding: 10px;
`

const P = props => (
  <p
    css={{
      margin: 0,
      fontSize: 12,
      lineHeight: '1.5',
      fontFamily: 'Sans-Serif',
      color: 'black'
    }}
    {...props}
  />
)

render(
  <div
    css={{
      backgroundColor: 'hotpink',
      '&:hover': {
        color: 'lightgreen'
      }
    }}
  >
    <P css={text}>This has a hotpink background.</P>
  </div>
)

最終的にどんなスタイルになるのか全然わかりませんよね?
自分にも分かりません。

書いている本人ですらシミュレートするのに一苦労なのですから、後から読んだ人には分かるはずもありません。
なので初めにチームでどの機能・オプションを使うのかをしっかり決めておくことが大事です。

これまでの設計から変わらないこと

既存のCSS設計(あるいはコーディングルール)から変わらないことも多くあります。

まずは、セレクタのスコープが絞られるようになったからと言って、要素セレクタや過度な入れ子はよろしくないということです。

Shadow DOMが実現出来ているわけではありませんので、要素セレクタでスタイルを宣言すると大抵何かと衝突します。
入れ子については記述が複雑になる他、パフォーマンスの観点でも良くありません。

あとは、最終的にアウトプットされるのはあくまでCSSです。
marginの相殺なんかはローカルスコープがあろうが発生してしまうもの。
そのためまだまだ最終的なCSSをイメージしながら書く必要はあります。

具体的なルール

ここまでは概念的な話がほとんどでした。
詳しく書きたいところなのですが、色々なライブラリが存在するので「これだ!」というものは無いと思います。

そんな中でもEmotionを使った詳細な書き方はこちらの記事で紹介しているので良ければ一緒に見てください。

まとめ

CSS in JSにおけるCSS設計の概念をまとめてみました。
まだベストプラクティスが出揃っていない分野だとは思うので、これから使う人にとって少しでも参考になれば幸いです。

Ateam Lifestyle Advent Calendar 2019 の2日目は、@tatsumin0206がお送りします!!どんなネタを用意してくるのか楽しみです!!

“挑戦”を大事にするエイチームグループでは、一緒に働けるチャレンジ精神旺盛な仲間を募集しています。興味を持たれた方はぜひエイチームグループ採用サイトを御覧ください。
https://www.a-tm.co.jp/recruit/

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

Reactチュートリアルでnpm start失敗したとき

はじめに

なんだかんだでReactチュートリアルをやったことがなかったのでやってみようと思ったのですが、npm startでエラーが出ちゃいました。ちなみにオンラインエディタではなくVSCodeを使っています。

環境

-Mac OS Catalina 10.15
-VSCode
-node 12.4.0

1.とりあえずエラーを見てみる

There might be a problem with the project dependency tree.
It is likely not a bug in Create React App, but something you need to fix locally.

Create React Appのバグじゃなさそうだよー、でもローカルで直さんとあかんやつやーって感じですね。

The react-scripts package provided by Create React App requires a dependency:

  "eslint": "^6.1.0"

Don't try to install it manually: your package manager does it automatically.
However, a different version of eslint was detected higher up in the tree:

という感じでどうやら上位ディレクトリのeslintのバージョンが違うのが原因ぽい。丁寧に解決法も教えてくれてるのでとりあえずそのとおりにやってみる。

2.node moduleの再インストール

To fix the dependency tree, try following the steps below in the exact order:

  1. Delete package-lock.json (not package.json!) and/or yarn.lock in your project folder.
  2. Delete node_modules in your project folder.
  3. Remove "eslint" from dependencies and/or devDependencies in the package.json file in your project folder.
  4. Run npm install or yarn, depending on the package manager you use.

In most cases, this should be enough to fix the problem.
If this has not helped, there are a few other things you can try:

  5. If you used npm, install yarn (http://yarnpkg.com/) and repeat the above steps with it instead.
     This may help because npm has known issues with package hoisting which may get resolved in future versions.

  6. Check if /Users/username/node_modules/eslint is outside your project directory.
     For example, you might have accidentally installed something in your home folder.

  7. Try running npm ls eslint in your project folder.
     This will tell you which other package (apart from the expected react-scripts) installed eslint.

とりあえず上記のとおりにやってみます。

1. Delete package-lock.json (not package.json!) and/or yarn.lock in your project folder.

プロジェクトフォルダからpackage-lock.jsonを削除します。

2. Delete node_modules in your project folder.

node_moduleをプロジェクトフォルダから削除します。

3. Remove "eslint" from dependencies and/or devDependencies in the package.json file in your project folder.

package.jsonからeslintを削除します。

4. Run npm install or yarn, depending on the package manager you use

npm installを実行します。

5. If you used npm, install yarn (http://yarnpkg.com/) and repeat the above steps with it instead.This may help because npm has known issues with package hoisting which may get resolved in future versions.

ここでまさかのnpmを使用した人はyarnを使ってもう1回4の手順をやるらしいです。何やらnpmにはパッケージの巻き上げに関する問題があるとか。それなら最初からyarnを指定してくれ...

とりあえず指示に従い、yarn installを実行します。yarnが入っていない人は先にyarnをインストールしておいてください。

6. Check if /Users/username/node_modules/eslint is outside your project directory.For example, you might have accidentally installed something in your home folder.

eslintがプロジェクトディレクトリ外にあるか確認をします。確認方法は7番のとおりにやればできます。

7. Try running npm ls eslint in your project folder.This will tell you which other package (apart from the expected react-scripts) installed eslint.

プロジェクトフォルダ内でnpm ls eslintを実行します。これによりeslintがインストールされている他のパッケージがわかります。

my-app@0.1.0 /Users/username/Documents/my-app
└─┬ UNMET DEPENDENCY react-scripts@3.2.0
  └── UNMET DEPENDENCY eslint@6.7.1 

依存関係がないと怒られました。
npm install react --save
を実行します。
そうすると、、、

my-app@0.1.0 /Users/username/Documents/my-app
└─┬ react-scripts@3.2.0
  └── eslint@6.7.1 

無事に解決!

3.npm install

ここまでできたらnpm installを実行します!
すると、、、

またエラーが出た!!!!!!!!!

エラーの内容は前回と一緒です。どうやらホームディレクトリのeslintのバージョンが怪しそう。調べたところeslintはグローバルインストール非推奨のようです。個別のプロジェクトごとにインストールするように。

ホームディレクトリのnode_modulesに移動し、rm -rf eslintを実行します。その後reactチュートリアルのディレクトリまで移動しnpm installを実行すると、、、

http://localhost:3000/が起動して画面が表示された!!!!!!!!!

まとめ

ここまで長かったですが、なんとか解決しチュートリアルが始められそうです。eslintはグローバルでインストールしないよう注意しましょう。また、エラーは英語だからと初学者は何かと避けがちですが、解決の糸口になる可能性が高いので頑張って読んでみましょう!

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