[メモ]Prettier

はじめに

prettierとtslintを組み合わせたら、良い感じのコーディングができるようになりました。
Reactでコーディングするとき用の設定メモとして残します。

Prettierの設定方法 on VSCode

1. Prettier pluginをインストールする。
Extensions(Ctrl + Shift + X)の検索ボックスで「Prettier」と検索する。
その後、「Prettier - Code formatter」というものをインストール。

2. VSCode上でPrettierを適用する。
settingsを開く。(Ctrl + Shift + Pより「> User Settings」と検索)
その後、「Text Editor→Formatting」より「Format On Save」にチェックをいれる。

3. プロジェクトでルールを設定する。
プロジェクトのルートに「.prettierrc」を作成します。
私の設定は以下の通りですが、お好みでどうぞ。

{
  "singleQuote": true,
  "jsxSingleQuote": false,
  "trailingComma": "all",
  "arrowParens": "always"
}

[React]Presentational+Container componentsについて

以前、同じタイトルで記事を書いたあとに、非公開にした記事を編集しようと思ったら、消えていたので再度投稿します。

はじめに

redditで以下の投稿があったので、自分の考えをアウトプットしようと思いました。
www.reddit.com

※参考
medium.com




React+Reduxでコンポーネントを書いているときに、コンポーネント思想の1つとしてPresentational+Container componentsがあります。

■Presentational+Container componentsの概要

Presentational component View部分を担当
Container component ロジック部分を担当

この思想によって、メンテンスのしやすいコンポーネントが出来ると思います。
ただ、参考に載せていたように、React開発に携わっているDanさんの言っていることもわかります。

Update from 2019: I wrote this article a long time ago and my views have since evolved. In particular, I don’t suggest splitting your components like this anymore. If you find it natural in your codebase, this pattern can be handy. But I’ve seen it enforced without any necessity and with almost dogmatic fervor far too many times. The main reason I found it useful was because it let me separate complex stateful logic from other aspects of the component. Hooks let me do the same thing without an arbitrary division. This text is left intact for historical reasons but don’t take it too seriously.
Presentational and Container Components - Dan Abramov - Medium

「hooksが出てきたので、もう分けなくていいんじゃないか」ってことですね。
そうですねーって感じです。hooksを使っていて思いますが、とにかくstate管理が楽です。Presentational+Container componentsでhooksを使うときは、正直view側に書きたいです。(viewでしかstate使わないし。。。storeにあるstateみたいなグローバル的なstateとは違うし。。。)


ただ、やっぱりPresentational+Container componentsの思想も個人的には捨てきれないです。(小規模アプリは除く)
以下の理由からです。
1. 1つのファイルにviewとlogicを書くと、とにかく長くなって見にくい。
2. viewとlogicを分けることによって、設計が楽になる。


私自身もどちらがいいというのは決定しきれていませんが、今の所はPresentational+Container componentsの思想でやっていこうと思います。
connect使おうが使わまいが、コンポーネント自体の再利用性、メンテナンス性をしっかり考えるべきだなと思いました。
(無駄にContainer作って、コンポーネントを増やのは嫌なので。。。)


また、記事アップデートします。
以上です。

[Webpack4.0] エラー解決集

はじめに

Webpackを使っているときのエラーとその解決方法をまとめます。
徐々に増やしていく予定です。

まとめ

テンプレート

エラー分 hoge
解決方法 resolve hoge
エラー分 ERROR in Entry module not found: Error: Can't resolve './src' in 'folder_name'
解決方法-1 entryを記述する。webpack4.0では、entryを指定していない場合、webpackが自動的に'./src'にエントリーポイントを探しに行ってしまうことが原因です。
解決方法-2 ファイル名の誤り。私はwebpack.config.jsがwebpack.config.dev.jsとなっていたため、発生しました。(ファイル名には気をつけましょう!!!!!!)
エラー分 Module build failed (from ./node_modules/ts-loader/index.js): Error: TypeScript emitted no output for 'file-name'
解決方法 tsconfig.jsonで["noEmit": true]と記載していると、発生します。削除しましょう。noEmitとは、ファイルを出力しないようにするための設定で、TypeScriptからもファイルを出力する場合、削除します。

typescript-fsaで複数の値を渡したい時

はじめに

typescript-fsaで複数の値を渡す方法で少し考えてしまったので、解法を残します。

今回は、Todoリストを追加するactionを例にして、必要な部分のみを記載します。
関連するファイルは以下の通りです。

  • actions.ts
  • reducers.ts
  • AddTodoContainer.tsx(container)
  • AddTodo.tsx(component)

actions.ts

// typescript-fsa
import actionCreatorFactory from 'typescript-fsa';

const actionCreator = actionCreatorFactory();

export const addTodoActions = {
  addTodoAction: actionCreator<{ name: string, priority: string }>('ADD_PATH'),
};

actionCreatorFactory()の型部分で、渡す値の型を記載します。
actionCreator()の型部分は、1つのargumentしか受け付けません。

reducers.ts

// typescript-fsa
import { reducerWithInitialState } from 'typescript-fsa-reducers';

// redux
import {
  addTodoActions
} from './actions';

export const addTodoReducer = reducerWithInitialState(initialStateAddTodo)
  .case(addTodoActions.addTodoAction, (state, payload) => ({
    ...state,
    paths: state.todos.concat(

      // addTodo()は、既存のTodoリストにconcatで新たなTodoをいれるための関数です。
      addTodo(payload.name, payload.priority) 
    ),
  }))

payloadにnameとpriorityが格納されているため、それぞれを既存のTodoリストに追加します。
(今回はaddTodo()がその役割です。)

AddTodoContainer.tsx

// redux
import { Dispatch } from "redux";
import { addTodoActions } from '../actions'

// react-redux
import { connect } from "react-redux";

// components
import AddTodo from '../components/AddTodo'

export type AddTodoFormHandler = {
  handleAddTodo(todoName: string, priority: string ): void
}

const mapDispatchToProps = (dispatch: Dispatch) => {
  return {
    handleAddTodo: (name: string, priority: string) => { dispatch(addTodoActions .addTodoAction({ name, priority })) },
  }
};

export default connect(
  null,
  mapDispatchToProps
)(AddTodo);

関数のそれぞれの引数を記載します。
(今回はnameとpriority)

AddTodo.tsx

<button
  onClick={() => {
    props.handleAddTodo(name, priority );
  }}
>
  Add Todo
</button>

onClickしたときに、containerのhandleAddTodoに入力したnameとpriorityの値を渡します。

React+Redux+TypeScriptでサンプルアプリを作成する。

はじめに

React + Redux + TypescriptでTodoアプリを作っていきます。
今回は、Todoの追加のみです。

ソースコード(GitHub)
github.com

参考にしたサイト
qiita.com
react-redux.js.org
qiita.com


環境

  • バージョン
node.js -v8.12.0
react -v16.9.0
redux -v4.0.4
react-redux -v7.1.1
  • フォルダ構成
.
|--/public
|    |--index.html
|
|--/src
|    |--/components
|    |    |--AddTodoForm.tsx
|    |    |--App.tsx
|    |    |--TodoList.tsx
|    |
|    |--/containers
|    |    |--AddTodoFormContainer.tsx
|    |    |--TodoListContainer.tsx
|    |
|    |--actions.ts
|    |--index.tsx
|    |--reducers.ts
|    |--store.ts
|
|--package.json
|--tsconfig.json
|--webpack.config.js

Action

// src/actions.ts
// typescript-fsa
import actionCreatorFactory from 'typescript-fsa';

const actionCreator = actionCreatorFactory();

export const todoArrayActions = {
  addTodoAction: actionCreator<string>('ADD_TODO'),
};

export const addTodoFormActions = {
  inputTextAction: actionCreator<string>('INPUT_TEXT'),
  initializeAddTodoFormAction: actionCreator('INITIALIZE_ADD_TODO_FORM'),
};

typesctipt-fsaを使って、actionを書いていきます。
todoの配列に関するtodoArrayActionsと、todoを追加するフォームに関するaddTodoFormActionsでアクションを分けました。
reducerでTodoArrayとAddTodoFormが分かれているので、こちらも分けました。

Reducer

// src/reducers.ts
// typescript-fsa
import { reducerWithInitialState } from 'typescript-fsa-reducers';

// redux
import {
  todoArrayActions, addTodoFormActions
} from './actions';

export type Todo = {
  id: number;
  text: string;
  done: boolean;
};

export type TodoArray = {
  todos: Todo[];
};

export const initialStateTodoArray: TodoArray = {
  todos: [
    {
      id: 1,
      done: false,
      text: 'initial todo',
    },
  ],
};

export type AddTodoForm = {
  inputText: string
};

export const initialStateAddTodoForm: AddTodoForm = {
  inputText: ''
};

let idCounter: number = 1;

const addTodo = (text: string): Todo => ({
  id: ++idCounter,
  done: false,
  text,
});

export const todoArrayReducer = reducerWithInitialState(initialStateTodoArray)
  .case(todoArrayActions.addTodoAction, (state, payload) => ({
    ...state,
    todos: state.todos.concat(
      addTodo(payload)
    ),
  }))

export const addTodoFormReducer = reducerWithInitialState(initialStateAddTodoForm)
  .case(addTodoFormActions.inputTextAction, (state, payload) => ({
    ...state,
    inputText: payload,
  }))
  .case(addTodoFormActions.initializeAddTodoFormAction, () => ({
    inputText: '',
  }))

type
大きく分けて、TodoArrayとAddTodoFormです。
reducers

  • todoArrayReducer

addTodoAction: 受け取ったTodoをconcatにて、配列の一番うしろに追加します。

  • addTodoFormReducer

inputTextAction: 入力されている文字をいれています。
initializeAddTodoFormAction: Todoを追加したあとに、ボックスの中身を初期化しています。

Store

// src/store.ts
// redux
import { createStore, combineReducers, compose, applyMiddleware } from 'redux';
import thunk from "redux-thunk";
import {
  TodoArray, todoArrayReducer,
  AddTodoForm, addTodoFormReducer,
} from './reducers';

export type AppState = {
  todoArray: TodoArray
  addTodoForm: AddTodoForm
};

const storeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

const store = createStore(
  combineReducers<AppState>({
    todoArray: todoArrayReducer,
    addTodoForm: addTodoFormReducer
  }),
  storeEnhancers(applyMiddleware(thunk))
);

export default store;

説明できるほど、わかっておりません。
以下の記事のstore.tsを参考にしてください。
qiita.com


index.tsx

// src/index.tsx
// react
import React from 'react';
import ReactDOM from 'react-dom';

// redux
import store from './store';

// react-redux
import { Provider } from 'react-redux';

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


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

Appコンポーネントに、Providerコンポーネント経由でstoreの値を渡しています。
特段、説明はいらないと思います。

Presentational and Container Components

このアプリの設計は、reduxの以下の設計思想に沿っています。
redux.js.org


AddTodoFormContainer.tsx

// src/containers/AddTodoFormContainer.tsx
// redux
import { Dispatch } from "redux";
import { todoArrayActions, addTodoFormActions } from '../actions'
import { AppState } from '../store';

// react-redux
import { connect } from "react-redux";

// components
import AddTodoForm from '../components/AddTodoForm'

export type AddTodoFormHandler = {
  handleAddTodo(value: string): void
  handleInputText(value: string): void
  handleInitializeAddTodoForm(): void
};

const mapStateToProps = (appState: AppState) => {
  return {
    inputText: appState.addTodoForm.inputText,
  };
};

const mapDispatchToProps = (dispatch: Dispatch) => {
  return {
    handleAddTodo: (value: string) => { dispatch(todoArrayActions.addTodoAction(value)) },
    handleInputText: (value: string) => { dispatch(addTodoFormActions.inputTextAction(value)) },
    handleInitializeAddTodoForm: () => { dispatch(addTodoFormActions.initializeAddTodoFormAction()) },
  }
};

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(AddTodoForm);

AddTodoFormに必要なactionとstateを記載しています。


TodoListContainer.tsx

// src/containers/TodoListContainer.tsx
// redux
import { AppState } from '../store';

// react-redux
import { connect } from 'react-redux';

// components
import { TodoList } from '../components/TodoList';

const mapStateToProps = (appState: AppState) => {
  return {
    todos: appState.todoArray.todos,
  };
};

export default connect(mapStateToProps, null)(TodoList);

TodoListに必要なactionとstateを記載しています。

AddTodoForm.tsx

// src/components/AddTodoForm.tsx
// react
import React from 'react';

// redux
import { AddTodoForm } from '../reducers';

// containers
import { AddTodoFormHandler } from '../containers/AddTodoFormConrainer';

type Props = AddTodoForm & AddTodoFormHandler;

const AddTodoForm: React.FC<Props> = (props: Props) => {
  return (
    <div>
      <input
        type='text'
        value={props.inputText}
        onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
          props.handleInputText(e.currentTarget.value)
        }}
      />
      <button
        onClick={() => { props.handleAddTodo(props.inputText); props.handleInitializeAddTodoForm() }}
      >
        Add Todo
      </button>
    </div>
  );
};

export default AddTodoForm;

AddFormのviewです。
inputのvalueはinputTextに設定しています。
onChangeにて、入力内容をdispatchしています。

TodoList.tsx

// src/components/TodoList.tsx
// react
import React from 'react';

// redux
import { TodoArray } from '../reducers';

type Props = TodoArray;

export const TodoList: React.FC<Props> = (props: Props) => {
  return (
    <div>
      {props.todos.map((todo) => (
        <li key={todo.id.toString()}>
          {todo.id}
          {todo.text}
        </li>
      ))}
    </div>
  );
};

TodoListのviewです。
mapで配列を順に描画しています。

自分のブレイクポイント

はじめに

自分でアプリケーションを作るときの、ブレイクポイントを書いておこうと思います。

ブレイクポイント

  • 481以下

モバイル

  • 481-746

モバイル大

  • 746以上

タブレット

  • 960以上

デスクトップ

公開したもの

はじめに

作成したアプリケーションをここにまとめます。

Applications

  • To-do application

https://infinite-tor-66450.herokuapp.com/

はじめて作成したTo-do application
Reactを使って何かを作りたいと思い、作成しました。
Front-end: React, Redux
Back-end: Node.js, MongoDB

NEW
glacial-tundra-36406.herokuapp.com
OLD
agile-beyond-84675.herokuapp.com


自身のポートフォリオサイト。
レスポンシブデザインについて、更に成長できたと思います。
Front-end: React, Redux
Back-end: Node.js