インターラプト開発者ブログ

インターラプトのエンジニアによる技術系ブログ

Nextjsでデータベースに接続する時に「Critical dependency: the request of a dependency is an expression」が出る

NextJsではWebpackを使用しているため、モジュールがWebpackに対応していないとCritical dependency: the request of a dependency is an expression というエラーが発生します。

NextJsでは、Sequelize や knex などでエラーが発生します。

これを回避するには、ウェブから読み込まれる部分とそうでない部分で明確にimportを分けると良いです。

importしたモジュールがWebpackに対応していない場合にそのページをWebから読み込んでしまうとエラーが発生するため、必ずWebpackがコンパイルせずにサーバサイドのみで実行するファイルで読み込みます。

DIを使っている場合は、明確にWebpack部分とバックエンド部分でDIコンテナを分けると良さそうです。

わかりやすいかも知れないReact.FCの使い方

前提

  • この記事ではTypeScriptを使用しています。
  • 17.0.1 のReactを使用しています。
  • Nextjs 10での動作確認をしています。

はじめに

ReactにはReact.FCという型定義があります。 こちらは、React.FunctionComponentが略された型で同じ型です。開発では殆どがReact.FCのほうが使われています。

昔のReactでは、React.SFC型が使われていましたが非推奨になっています。

React.FCとは

React.FCは、constによる型定義でコンポーネントを定義できる型です。コンポーネントというのはReact独自のタグで、オリジナルのタグを作成し、タグの中で他のタグをまとめて定義できるものです。

const MyTag: React.FC = () => {
  return <div>こんにちは</div>;
};

const Container: React.FC = () => {
  return <MyTag />;
};

コンポーネントの中は、returnで返ったタグがレンダリング(描画)されます。constで定義したタグはそのまま他のコンポーネントで呼び出して使う事ができます。

複数タグを描画する

基本的にはreturnで返るタグはひとつだけですが、<></>で囲むことで、複数タグをレンダリング出来ます。

 const MyTag: React.FC = () => {
  return <>
    <div>こんにちは</div>
    <div>こんにちは</div>
  </>;
};

こうすることで、親要素が消えて、子要素のみが直接描画されます。

また、親タグをひとつだけ用意しても同様のことが出来ますが、一階層余分な要素が増えます。CSSなどの定義で、階層によって描画が変わる場合に便利です。

 const MyTag: React.FC = () => {
  return <div>
    <div>こんにちは</div>
    <div>こんにちは</div>
  </div>;
};

タグに属性値を渡す

前項では、ただコンポーネントを呼び出しただけなのでコンポーネントの中身を書き換えることができません。コンポーネントに属性値を渡してその内容をレンダリングしてみます。

const MyTag: React.FC<{ title: string; }> = (props) => {
  return <div>{props.title}</div>;
};

const Container: React.FC = () => {
  return <MyTag title="こんばんは" />;
};

MyTagコンポーネントに、titleという属性を付けました。 TypeScriptなので、肩定義にもtitle: string;がついています。 これはReact.FCがジェネリクス型となっており、引数にどのような属性値が含まれるのか定義することができるようになっています。

const MyTag: React.FC<{ isAm: boolean; t1: string; t2: string; }> = (props) => {
  return <div>{props.isAm ? props.t1 : props.t2}</div>;
};

const Container: React.FC = () => {
  return <MyTag t1="おはよう" t2="こんにちは" isAm={true}/>;
};

属性値には複数の引数を定義することができます。 属性値の型にはstring以外にも様々な型を渡すことが可能で、どんな型でも入れることが出来ます。

文字列の場合は、""で定義しますが、変数やそれ以外のリテラル型やboolean型(true/falseの真偽値)は{}で囲います。

属性値はpropsという変数にオブジェクトとして格納されていて、ドットで呼び出すことができます。Reactでは慣習的にpropsという名前の変数が使われているので、合わせておきます。

この実装の中で下記のような部分があります。

props.isAm ? props.t1 : props.t2

こちらは三項演算子という条件式で、1項がtrue(真)だった場合は、2項を、偽だった場合は三項を返します。 isAmは午前中かどうかと言う意味の真偽値です。

propsを省略する

毎回propsを定義するのは使い勝手が悪いので、直接propsの中の属性値を呼び出せるようにします。

const MyTag: React.FC<{ title: string; }> = ({ title }) => {
  return <div>{title}</div>;
};

const Container: React.FC = () => {
  return <MyTag title="こんばんは" />;
};

propsが { title: string; } に置き換わりました。 こちらは、propsの中にあるtitleを取り出して定義することができる分割代入という方法で、Destructuring assignment 構文と言います。

分割代入は他にも様々なパターンがあります。

const [a, b] = [10, 20];
const { a, b } = { a: 10, b: 20 };

propsを何度も呼び出して使うと、見通しが悪くなるので、分割代入を使うようにすると良いです。

分割代入には別名をつけることも可能です。

const { a: newA, b } = { a: 10, b: 20 };

また定義されていなかった場合、初期値を設定することも可能です。

const { a = 30, b } = { a: 10, b: 20 };

コンポーネントのpropsをそのまま子コンポーネントに渡す

コンポーネントのレイアウト調整などで複数のコンポーネントに階層化させて実装した場合、コンポーネントを共通化させたい場合があります。

その場合は、...props と書くことで渡すことが出来ます。

const MyTag: React.FC<{ title: string; }> = ({ title }) => {
  return <div>{title}</div>;
};

const Container: React.FC<{ title: string; }> = (props) => {
  return <MyTag ...props />;
};

分割代入しているとpropsがなくてエラーが出るので、その場合は、下記のようにします。

const MyTag: React.FC<{ title: string; }> = ({ title }) => {
  return <div>{title}</div>;
};

const Container: React.FC<{ title: string; }> = (props) => {
  const { title } = props;
  console.log(title);
  return <MyTag ...props />;
};

引数内で分割代入すると使い勝手が悪いので、最初の行で分割代入をする場合もありますが、状況に応じて使い分けると良いです。

子要素を引数として受け渡す

HTMLタグでは他の要素をタグで包むことが出来ます。

<p>子要素</p>

Reactではこの子要素をpropsの中に引数として持ち、コンポーネントの内側で描画することができます。

子要素は、props.children としてアクセスすることが出来ます。

const MyTag: React.FC<PropsWithChildren<{ title: string; }>> = ({ title, children }) => {
  return <div>{title}: {children}</div>;
};

const Container: React.FC = () => {
  return <MyTag title="こんばんは"><strong>子要素</strong></>;
};

PropsWithChildren型は<{...}>で囲まれている属性値値の他に、childrenを含んだpropsである型を持ちます。

この型を使うことで、childrenを使うコンポーネントであることがわかりやすくなります。

React.VFC

こちらは逆でchildrenを含まないコンポーネントを定義するのに使用します。childrenを使用していないことを明示的に表したい場合は、こちらの型を使用します。

refの転送を行う

Reactコンポーネントでは、属性にrefを使うとタグのDOM要素を取得できます。直接DOMに対して何か処理を行いたい場合はrefを使います。

const ref = useRef(null);

const Container: React.FC = () => {
  return <div ref={ref}>node</div>;
};

このrefですが、Reactコンポーネントに対して使用すると、本来refを使用したい要素にうまく引き渡せない場合があります。

const ref = useRef(null);

const MyTag: React.FC<{ title: string; ref: any; }> = ({ title }) => {
  return <div ref={ref}>{title}</div>;
};

const Container: React.FC = () => {
  return <MyTag ref={ref}>node</div>;
};

上記の例ですと、refはうまく引き渡せずに、div要素のrefは取得できません。MyTagそのもののrefを取得してしまうからです。

この場合は、forwardRefを使うことで解決できます。

const MyTag = React.forwardRef((props, ref) => (
  <div ref={ref}>node</div>
));

fowardRefはその名の通りrefの転送を行ってくれます。 親要素でrefが指定された場合、親要素のrefを取得するのではなく、そのままコンポーネントの引数として渡すことが出来ます。

絶対に再レンダリングさせたくない場合のコンポーネント活用

TL;DR

絶対に再レンダリングさせたくない

jQueryなどで直接DOM操作しているライブラリをReactから呼び出して使うと、すこし困ったことがあります。Reactはコンポーネントが管理しているStateに変化があると、コンポーネントの持つHTML(JSX)を再レンダリングするのですが、jQueryなどでレンダリングした部分が消えてしまうためもう一度jQueryレンダリングを行う必要があります。

この際に、jQueryライブラリが内部で持っている状態(State)が消えてしまい、表示が初期状態に戻ってしまうことがあります。 renderを実装しているjQueryライブラリであれば、この問題を回避することが出来ますが、多くのライブラリではインスタンスを作成するときにしかレンダリングを提供していないことが多いと思います。

この場合出来るだけReact外のレンダリングを持つインスタンスの初期化を避けた方が良いです。

useMemoを使ってレンダリングされないElementを取得する

useMemoは、値をメモ化し不要な再計算を行わないように値を返してくれます。この中でjQueryライブラリ等を実行するだけのコンポーネントを呼び出します。( ※ コンポーネントの引数が無い場合でも同様の効果が得られますが、初回レンダリングで値を渡しレンダリングする必要があるため、useMemoを使った方が管理しやすいです)

const graph = useMemo( () => <Graph list={list} />, [] );

useMemoは第二引数に [] を指定すると初回のみレンダリングされます。list変数は初回の値のみメモ化されるため、listの値を変更してもGraphには新しいlistの中身が伝わりません。

これで再レンダリングを防ぐことが出来たので、あとはlistの更新方法を考えるだけです。もちろん useMemoの第二引数にlistを入れる方法は、listが更新されるたびにGraphが再レンダリングされるため使えません。

この場合Graph内でaddEventListener() でイベントの監視をして、親要素から値の変更を検知したらイベントを送るように実装します。

そのためは、まずjQueryなどで定義されたElementのrefを取得します。

refの転送を行う React.fowardRef

React.fowardRefは、コンポーネントにrefを指定すると、そのコンポーネントのrefではなく子コンポーネントのrefが参照されるようになります。

const Graph = React.forwardRef((props, ref) => (
  <div ref={ref} className="graph">
    {props.children}
  </div>
));

ref.currentの中身にはレンダリング済みのHTML DOMが入っており、このDOMに対して dispatchEvent() することで直接jQueryライブラリがレンダリングしたDOMに対してイベントを発火させることができます。

値を持たせたイベントを発火する

Eventを発火させるには、Eventを継承してカスタマイズしたものを利用しますが、もっと楽にカスタムイベントを作成できる機能が用意されています。

const event = new CustomEvent('updateList', { detail: list });

CustomEvent は 引数に { detail: any; } を持ち、任意のパラメータを渡せます。これによってlistを渡し、レンダリングを避けたい子コンポーネント側でイベントを購読し、ライブラリ内部による状態変更を行い、DOMを変更し、再レンダリングさせることなく状態変更出来ます。

補足

  • refのイベントを監視しなくても初めからライブラリが用意しているイベントを発火しても良いです。
  • Nextjsなのでホットリロードすると、二重でイベントを監視してしまうことがあるため、.removeEventListener してから.addEventListener します。

自己流記憶術

代表の高橋です。昔からやっている記憶術について話したいと思います。

自分は小学生の頃からプログラミングをやっているのですが、ノートを取るのが苦手で覚えたいことをメモ帳でまとめておくことが多かったです。

授業中、パソコンでメモできたら絶対頭良くなってたのにと思う日々でした。左利きな事もありとにかくノートにメモする事が苦手でした。

そこで開発したのがノートアプリでした。検索ができて、すぐにメモした内容にアクセスができるアプリです。まだEvernoteもない時代からパソコンでメモする習慣が出来ました。

プログラミングするときも、C言語について勉強したときも自前のメモアプリに記録していつでもコードを使えるようにしておく事で素早くプログラミングが出来る様になっていました。

後になってからそれを「スニペッドコード」と言うことを知りました。

次に活かせるナレッジ蓄積ルール

自分は知識を蓄積する時に、必ず意識していることがあります。

  • メモアプリは 1秒以内に起動できる事
  • メモアプリは 3秒以内に検索できる事
  • メモアプリは 5秒以内に目的の情報にたどれる事
  • クリップボードの情報はショートカットで貼り付けられて、先頭の行がタイトルになること
  • 必ずタイトルをつける事

小学生の頃からこのルールを守りつづけで、今では起業し順調に成長する事ができるようになってきました。

ナレッジは資産であり、多ければ多いほど良いのですが、1000を超えてくると重くなってくる物が多いです。そう言う時はファイルを分けて管理すると良いです。

  • Swift
  • Object-C
  • Ruby

のように関心事ごとにファイルを分けて管理します。

脳内にはインデックス「だけ」を作る

人は何か作業する時にワーキングメモリーに詰め込めるだけ詰め込んで作業の効率をあげますが、限界があります。ワークフロー全体を見るのか、ワークフローの一部分だけを見るのか分けて、ワーキングメモリーを切り替えて作業します。

それでも、ながら作業やかなり大きな範囲を一度にやると、ワーキングメモリーから溢れてしまい漏れが発生したり、無気力になったりします。

こう言う時は、覚えておきたいことをメモしておいて、中身については忘れてしまい、メモの「タイトル」だけを覚えておきます。必ずメモにタイトルをつける必要があるのは、記憶のインデックスを作るためです。

インデックスだけをワーキングメモリーに詰め込んでおくと、かなりの量を把握する事ができるようになります。実際には全部覚えているわけではなく、タイトルだけ思い出して、検索をかけます。タイトルから内容を見つけるまでに5秒以内で出来るように環境を構築しましょう。

ツリー構造を駆使しよう

メモアプリの中にはツリーで管理できるものがあります。物によっては2段までしかない物がありますが、何層でもツリーをネストできるものを使うと良いです。

知識は関連したものを集約することで、まとめてインデックスできるし、記憶の箱にアクセスする事ができます。ツリーの整理はそのまま記憶の整理になるのです。

逆引きを作ろう

メモアプリを使う際に、単純な項目別のメモをまとめるよりも「逆引き」としてメモした方が記憶の整理に繋がる場合があります。

逆引きというのはタイトルの付け方の工夫のことで、「スクロールバーの背景色を変える方法」といった付け方をします。この記憶の仕方の凄いところは、自分でこのようなタイトルを付けたことによる記憶が、ただコピペしただけよりも格段に強く記憶に焼き付けられることです。

内容はコピペでもいい場合がありますが、できる限りタイトルは手動で書いてみてください。忘れずに覚えておけます。

ワーキングメモリーが溢れたら

ワーキングメモリーが溢れると、一時的に無気力に襲われることがあります。こうなったら良くない状態が長く続くので、一度作業をやめて、今頭にあることを全部書き出して、寝たり遊んだり、美味しいものを食べたりしてください。

リフレッシュして、作業に取り掛かると良いです。もちろん忘れてしまっても、ちゃんとメモしてあるから安心です。忘れて良いと思える事が、ワーキングメモリが溢れた時の対処方法です。

絵文字を使おう

メモする時に、タイトルの先頭に絵文字をつかうだけです。 どうしても覚えておきたい場面で有効です。

Slackで高頻度のリマインドが便利

代表の高橋です。

普段時間を効率よくつかうことを常日頃から意識しています。時間管理には普段はTodoistを使っていますが、Slackと「あるプラグイン」を使った方法も組み合わせるともっと効率があがりそうだということに気づきました

Slacountで高頻度にリマインドを行おう

Slacount - Custom Slack Countdowns & Timers https://wrugo.com/slacount

SlacountはSlackアプリで、Slack上でコマンドでリマインド登録してくれます。どのチャンネルでも使用することができ、自分がしかいないチャンネルでも利用することができます。

使い方

使い方はとってもかんたんで、コマンド + タスク名 + 何時間後 といった形式で入力出来ます。

/slacount_create  コーヒー飲む 10 minutes
/slacount_create "コーヒー 牛乳" 100 minutes
/slacount_create "タスク名" 3 weeks "3週間たちました"

他にも繰り返し設定などもあるようです。 確認方法は /slacount_list でタスク一覧が表示されます。

メリット

リマインドをSlack上で高頻度に管理することで、より時間に縛りができて、時間効率がよくなります。更に、「思ったよりも時間がかかってしまった」「思ったよりも早くおわった」という振り返りも直接Slack上でメモしておけたり、過去の作業をSlack上で「検索」できるのも良いですね。

/remind との違い

Slackの公式コマンドには /remind がありますがこちらは特定のチャンネル or 個人名を指定しないといけなかったり、通知先がDMだったりするので何かと不便です。わざわざチャンネル名を指定するのが非効率ですよね。

f:id:interrupt_inc:20201214191651j:plain

この、 [@username or #channel ] を省略できればいいのですが・・。

リードのようでリードじゃない話

以前からシステム開発のリード獲得サービスを使っています。使っていて気付いたのは、プル型のインサイドセールスというよりはどちらかというとプッシュ型の側面が強かった。

しかしリードの入り口は明らかにプルなのです。だから弊社はプル型の営業にあうようにヒヤリングを行ったり実績や出来ることなどから、求めていることに対してご提案させて頂くのですが、どうも噛み合わないことが非常に多かったのです。

お客様の解決したい課題があり、その課題を解決するご提案をするには情報が少ない状態からスタートするためヒアリングが必須なのですが、ヒアリングするまでたどり着けないことが多く、まずは弊社としての商品説明や強みを紹介することを求められます。

リード獲得の一次ヒヤリングを弊社がおこなっていない事によるケアレスミス これは恥ずかしながら後から気づいたことですが、リード獲得サービスを利用しているということは一次ヒヤリングは弊社ではなくそのリード獲得サービスが行なっています。そのため、「具体的な話はすでに伝えているはずだからその課題解決の商品提案をしてくれるはずだ」と期待しています。

それが、実際には期待していたものが出てこなかったとなるため、そこで失注となってしまうのです。自分が発注者であれば、自分も同じことをしてしまったかもしれません。

リードは見込み顧客

本来リードは見込み顧客という意味ですが、そこには弊社に依頼する期待があると思います。が、本当の意味で期待をしてリードになると思いました。

何度もお客様の期待を裏切る結果になってしまったことが多く申し訳なかったと思っています。沢山のお叱りと受けて、少しずつ成長してきました。

本当のリードを獲得するためにこれからすること 弊社はこれまでさまざまなご提案をさせて戴いてきましたが、それは直接お客様が弊社に依頼したいという時にこそやるべきことだったと思います。 お客様の期待を裏切らないことをまず第一に考えるため、少しプル型の営業スタイルをやめることにしました。商品を持ち、プッシュ型の営業を強化し、弊社商品に期待して頂くことに全力を注ぎたいと思います。

結局のところ、本当のリード獲得は自社商品の期待だったのです。ご提案とは、その自社商品に絡んだ提案ということです。

商品に絡んだ提案が本当の提案

技術力を武器にした会社、DXの会社、様々な会社があります、一見すると凄そうですが、何ができるかわかりません。弊社もそうです。

大企業であれば、ブランド力によって提案のみで受注可能ですが、中小にとっては商品が看板になります。そこは大前提で、リード獲得サービスは、その次だった、というお話しでした。

D2C販売モデルとコミュニケーションの深い関係

楽天Amazonマーケットプレイスとは違い、自社のEC上から直販できるD2Cの販売モデルが伸びてきています。でも結局これって昔からある自前のECサイトに過ぎなくて、どんなところがメリットがあるのかよくわからない点が多くあります。

D2C販売モデルをただなんとなくでやってしまわないように、気をつける点を書いていきます。

これまでの自前のECサイトとの違い

D2Cは、Direct to Consumer の略で、楽天Amazonといったモール型のストアを経由せず直接顧客にメーカーが販売ができるという販売モデルです。特に、リアル店舗での直販ではなく、ネット上での直接販売のことを指します。しかしこれだけでは、ECサイトを作るだけなのと変わりありません。「ダイレクト」とという単語には、直接顧客とメーカーがコミュニケーションが取れるようになったということがこれまでの販売モデルと大きく異なる点になります。

メーカーは製造した製品を販売するには、卸業者や小売店を介して消費者の元に届けます。メーカーは直接販売したくても、メーカーの生産工場が海外にあったり郊外にあるため困難でした。

販売経路としても、間に複数社挟んであるため、メーカーの声は直接顧客には届きにくい状態でありました。ECサイトも直接メーカーが運営していない場合、売られている製品の良さも伝わりづらいままです。

どちらもECサイトではありますが、D2C販売は、メーカー直販であるからこそ顧客との距離が近く、コミュニケーションが取りやすいために、顧客満足度が向上すると言った点が大きな違いとなります。

直販だからこそできるマーケティング施策

D2C販売モデルの場合、直販のため中間業者が存在しないので手数料が大幅に浮きます。決済代行業者やECサイトのインフラ費用はありますが、そこまで大きな額ではありません。この浮いたマーケティング予算を、メーカーが自分たちで自由に予算を決め、企画、実行出来るのが大きなメリットです。

例えば集客のために公式のソーシャルアカウントを作ることが出来ます。TwitterInstagramが良いでしょう。これが小売店であれば、どうしても非公式アカウントになってしまいますし、勝手にブランドを語って集客していることになるので怒られてしまうかもしれません。公式アカウントを作れるのは直販だけなのです。

ソーシャルアカウントでは直接顧客とコミュニケーションを行い、口コミなどを投稿を促すためのキャンペーンを打っても良いです。そうした口コミはECサイト上に埋め込むこともできます。

顧客の声を直接聞くことが何よりも大事

D2Cモデルを成功させるには、メーカーがサイトに訪れる顧客の声を聞くことが何よりも大事になります。リプライで直接販売声が聞けるソーシャルメディア運用が効果的ですが、チャネルはソーシャルメディアだけではないので、サイトに訪れる顧客全体から直接聞く必要があります。

ちょっと恥ずかしいですが、当たり前の事を今から言います。「ただ売るだけと思ってはいませんか?」ということです。サイトを作って商品を並べる、キャンペーンで割引、クーポンで値引きだけでは、まだただ売っているだけの範疇です。

私はリアル店舗で4年ほど働いていた経験がありますが、売り込みも大声でやったり、どの商品がよいかお客様が迷ってそうでしたら声をかけて「こちらがおすすめですよ」と提案していた事もありました。ECサイトも同じことで、直接顧客とコミュニケーションを取れば良いです。 顧客は商品を購入する前に、必ず悩んでいますから、そこでその商品を作った本人と話ができれば満足して商品を購入できるでしょう。

質問コーナーを作ろう

顧客が商品について不明点があれば気軽に投稿して質問ができるミニ掲示板をつけると良いです。公開したくない人もいるかもしれないので、非公開のチェックボックスはあったほうが良いかもしれません。この機能は、Amazonにもありますね、回答者は卸業者であることが多いのであまり嬉しくは感じませんでしたが...

口コミコーナーを作って返信しよう

口コミとその口コミに対して返信します。そして公開しましょう。生協やイオンのご意見コーナーに近いですね。ソーシャルメディアでなくても、フォームがあれば、簡単につくることができます。

使ってみた(食べてみた)レポート投稿機能

口コミとは似てますが、キャンペーンと併用して、商品を使用したレポートを書いてもらいましょう。口コミよりも商品のファン(リピーター)が書いてくれる事が期待できます。

ファンを集めよう

メーカーには開発者(生産者)がいることをちゃんと顧客は理解しています。中の人は職人とも言えます。メーカーとしてのブランド戦略としてロゴやデザインを意識しがちですが、私は人そのものがよりブランドになっていくと思っています。

どういう想いでこの商品を作ったのか、ブランド名やロゴに込められた理由は何かを開発者(生産者)が直接語るのです。1ページ丸々使って語りましょう。

クラウドファウンディング

ファンが集まり、ソーシャルメディアのフォロワー数も増えたらクラウドファウンディングを利用する選択肢が増えます。ここまでくると、沢山の顧客の声が集まっていますから、顧客が欲しいものがわかってきていると思います。クラウドファウンディングが成功する確率が非常に高い状態と言えるでしょう。

誰のためにあなたは何ができる?

D2Cの販売戦略は突き詰めるとこれだと思います。小売店に売ってもらうだけでは、商品の想いが届かなかった「誰か」が必ずいたと思います。インターネットを通して、とても距離が近くなったからこそ、これまで届けられなかった誰かに出来ることはいっぱいあるんじゃないかなと思います。