20210915のReactに関する記事は9件です。

初学者がReactの開発環境を整える

はじめに JavaScriptでWebアプリ開発する際のフレームワークReactを勉強し始めたので忘れないように記録する。 今回はReactでWeb上でHello World!と表示させる手順ついて記録する。 間違っているところがありましたらご指摘していただけると幸いです。 事前準備 React公式サイト Chromeの拡張機能 Chromeでreact developer toolsと調べるか下記のURLから拡張機能を追加しておく。 React Developer Tools さらにReact Developer Toolsの拡張機能の設定でファイルの URL へのアクセスを許可するをONにする必要がある。 開発 React読み込み 公式サイトからDoc→Getting Started→Web上で試せるオンラインエディタのこのHTMLファイルを開きheadタグ内のScriptタグ2つを自身のhtmlファイルのhead内へコピペする。 公式サイトからDoc→既存のウェブサイトにReacrを追加する→JSXを手軽に試してみるのScriptタグを自身のhtmlファイルのhead内へコピペする。 または、下記の3つのコードをコピペする。 react_load <script src="https://unpkg.com/react@17/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script> 下記のScriptタグは本番環境では使用しないでください。 react_load <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> 1番目がReact本体のScriptタグ、2番目がReactの結果をブラウザのDOMに反映させるためのScriptタグ、3番目がJSXなどの やJavaScriptの新しい文法を使うためのBAbelというライブラリのScriptタグである。 自分のScriptコードを書くための準備 scriptタグはReactの読み込みで出てきたBabelを使って変換していくので、type属性を付ける。 ローカルファイルでの開発は自身のscriptを別ファイルにするとエラーになる my_script <script type="text/babel"></script> Hello World!と表示させる 現在のhtmlファイル内のコード react_practice <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>React Practice</title> <link rel="stylesheet" href="css/styles.css"> <!-- react読み込み --> <script src="https://unpkg.com/react@16/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script> </head> <body> <script type="text/babel"> </script> </body> </html> Reactを使ってUIを表示させる。 まず、UIを表示させるための領域を生成する。 JavaScriptで部品を判別するためにidを付け、idはrootとする。 place_create <body> <!-- UI表示のためのdiv --> <div id="root"></div> <script type="text/babel"> </script> </body> </html> このdivにReactで作った部品を描画させる。 即時関数で囲いたいのでアロー関数式を記述する。 arrow <script type="text/babel"> (() => { })(); </script> ReactDOM.renderとし、第一引数に描画したいUIをJSXという記法で書く。 ui_display <script type="text/babel"> (() => { ReactDOM.render( <h1>Hello World!</h1>, ); })(); </script> 第二引数にはUIをどこに描画するかを指定する。 今回はidがrootの場所に描画する。 ui_display <script type="text/babel"> (() => { ReactDOM.render( <h1>Hello World!</h1>, document.getElementByID('root') ); })(); </script> 現在のhtmlファイル内のコード react_practice <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>React Practice</title> <link rel="stylesheet" href="css/styles.css"> <!-- react読み込み --> <script src="https://unpkg.com/react@16/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script> </head> <body> <div id="root"></div> <script type="text/babel"> (() => { ReactDOM.render( <h1>Hello World!</h1>, document.getElementByID('root') ); })(); </script> </body> </html> ここまでのファイルをWeb上で表示させるとHello World!と表示される。 これで開発環境が整えられた。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Redux ToolitのRTK Queryについて

RTK Queryおもしろそうなので調べてみたり触ってみたりした。 どちらかというと雰囲気を伝えることを目的とした記事で、勘違いしている可能性もあるかもしれないので(つっこんでほしい) 詳細については公式を読んでください。 対象 Reactを使ってるひと Reactでのデータ取得のキャッシュの扱いとか管理がしんどいなってひと Reduxは分からなくてもOK RTK Query is 何? Redux Toolkitのチームが作ったデータ取得とキャッシングのためのツール。 データを取得するのが楽になる。キャッシュのロジックを手書きする必要がなくなる。 最近のReactコミュニティでは「データの取得とキャッシュ」は「状態管理」を別のものとして管理したいという需要があった。 → RTK Queryは「データの取得とキャッシュ」に特化したツール。(Reduxは「状態管理」に特化したツール) 実際のデータ取得とキャッシングのロジックは、Redux ToolkitのcreateSliceとcreateAsyncThunkのAPIの上に構築されている。 → いい感じに隠蔽されているため、ThunksとかReducerとか書く必要がない。 → ReduxとかRedux Toolkitとか分からなくても使える 簡単にいうと Hooksを使って簡単に非同期データ取得。 キャッシュの実装もいろんなパターンを簡単にできる。 環境構築 create react appでredux-typescriptテンプレートを使えば最初から入っている。 npx create-react-app my-app --template redux-typescript もちろん既存プロジェクトに追加でもOK。 npm i -S @reduxjs/toolkit 使い方 Query Endpointsの定義 createApiを使い、endpointsに、builder.queryメソッドを使用してフィールドを定義する。 たとえば、 ユーザを全取得するgetUsersとユーザを1件取得するgetUserを定義したい場合、以下のように書く。 import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' export interface User { id: string name: string } type Users = User[] export const userApi = createApi({ baseQuery: fetchBaseQuery({ baseUrl: 'http://example.com/api/v2/' }), endpoints: (builder) => ({ getUsers: builder.query<Users, void>({ query: () => `users`, }), getUser: builder.query<User, string>({ query: (userID: string) => `users/${userID}`, }), }), }) // use + endpointsで設定した名前 + QueryでHooksが作られる export const { useGetUsersQuery,useGetUserQuery } = userApi use + endpointsで設定した名前 + QueryでHooksが作られるため、これをExportしておく。 Storeに設定 reducerとmiddlewareに追加する。これでキャッシュをredux内でいい感じに管理してくれるようになる。 import { configureStore } from '@reduxjs/toolkit' import { userApi } from './service/user' export const store = configureStore({ reducer: { [userApi.reducerPath]: userApi.reducer, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(userApi.middleware), }); index.tsxとかでstoreをReduxのProviderに設定 import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; import { store } from './app/store'; import { Provider } from 'react-redux'; ReactDOM.render( <React.StrictMode> <Provider store={store}> <App /> </Provider> </React.StrictMode>, document.getElementById('root') ); Hooksを利用してデータを取得する Hooksを使ってデータ取得を行うことができる。 import React from 'react' import { useGetUserQuery } from './app/service/user' const App: React.FC = () => { const { data, error, isFetching } = useGetUserQuery('A001') return ( <div> {error ? ( <div>エラー</div> ) : isFetching ? ( <div>ロード中</div> ) : data ? <div>{data.name}</div> : <div>データなし</div>} </div> ); } export default App; Hooksの返却値はこんな感じ * data : クエリで取得したデータが入る * error : エラーが発生した場合、エラーの値 * isUninitialized : まだクエリが発行されていない場合、true (後述) * isLoading : クエリが初めて発行されていて、返却されていない場合true * isFetching : クエリが発行されていて、返却されていない場合true * isSuccess : クエリが成功したデータがある場合true * isError : クエリにエラーがある場合true * refetch : 再度クエリを実行するための関数 isLoadingとisFetchingの違いは、isLoadingは初回のクエリ発行のタイミングのみ走るものであるということ。 → isLoadingはページ読み込み時にスケルトンを出すときとかに使うイメージ キャッシュの動作について Hooksを利用したデータ取得ではキャッシュが利用される。 基本のキャッシュ RTK Queryでは基本のキャッシュを APIエンドポイント シリアル化されたクエリパラメータ アクティブなサブスクリプションの参照数 で管理する。 同じエンドポイントで同じクエリパラメータのクエリが別で実行されている場合にはクエリの発行を行わず、キャッシュデータを利用する。 const Component1: React.FC = () => { const { data } = useGetUserQuery('A001') return <div>...</div> } const Component2: React.FC = () => { const { data } = useGetUserQuery('A002') return <div>...</div> } const Component3: React.FC = () => { const { data } = useGetUserQuery('A002') return <div>...</div> } たとえばこんな感じのコンポーネントがあってすべてマウントされた場合、Component2とComponent3は同じエンドポイントで同じクエリパラメータのため、どちらかではキャッシュが利用される。 キャッシュデータはアクティブなサブスクリプションがある限り残り、アクティブなサブスクリプションがすべてなくなって60秒経つと削除される。 上記の例だとComponent2とComponent3がアンマウントされてから60秒経つとキャッシュが削除され、またマウントされるタイミングでクエリの発行が行われる。 なんか分かりづらいので簡単に言うと、同じクエリを別のところで投げている場合、自動的にキャッシュが利用されるということ。 refetch refetchを行った場合、キャッシュを無効にして必ずデータの再取得を行う。 import React from 'react' import { useGetUserQuery } from './app/service/user' const RefetchComponent: React.FC = () => { const { data, refetch } = useGetUserQuery('A001') return ( <div> <div>{data && data.name}</div> <button onClick={() => refetch()} > データを再取得する </button> </div> ); } export default RefetchComponent; refetch後、取得データはキャッシュされ、同じエンドポイントで同じクエリパラメータを利用しているHooksのdataもその値になる。 キャッシュの設定変更 オプションを設定するとキャッシュのタイミングなどを調整することができる。 keepUnusedDataFor アクティブなサブスクリプションがすべてなくなって60秒経つと の60秒の部分を変えられる。numberで秒数指定する。 import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' export interface User { id: string name: string } type Users = User[] export const userApi = createApi({ baseQuery: fetchBaseQuery({ baseUrl: 'http://example.com/api/v2/' }), // APIに対するglobalな設定値。ここに書くとendpoints全てに反映される。 keepUnusedDataFor: 30, endpoints: (builder) => ({ getUsers: builder.query<Users, void>({ query: () => `users`, }), getUser: builder.query<User, string>({ query: (userID: string) => `users/${userID}`, // ここに書くとクエリ毎に設定できる。 keepUnusedDataFor: 5, }), }), }) export const { useGetUsersQuery,useGetUserQuery } = userApi refetchOnMountOrArgChange デフォルトの動作よりも頻繁にデータ再取得したいときに設定する。 booleanかnumberを設定する falseを設定した場合 : デフォルトと同じ。 trueを設定した場合 : クエリに新しいサブスクライバが追加されたときにクエリが常に再実行されるようになる。 numberを設定した場合 : クエリに新しいサブスクライバが追加されたとき、最後にクエリが実行されてから指定した数字の秒数が経過していた場合にクエリが常に再実行される。 import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' export interface User { id: string name: string } type Users = User[] export const userApi = createApi({ baseQuery: fetchBaseQuery({ baseUrl: 'http://example.com/api/v2/' }), // APIに対するglobalな設定値。ここに書くとendpoints全てに反映される。 refetchOnMountOrArgChange: true, endpoints: (builder) => ({ getUsers: builder.query<Users, void>({ query: () => `users`, }), getUser: builder.query<User, string>({ query: (userID: string) => `users/${userID}` }), }), }) export const { useGetUsersQuery,useGetUserQuery } = userApi const Component2: React.FC = () => { // 個別のクエリに設定することもできる const { data } = useGetUserQuery('A002',{ refetchOnMountOrArgChange: true }) return <div>...</div> ちょっとわかりづらいので、trueを設定した場合の挙動について説明する。 const Component2: React.FC = () => { const { data } = useGetUserQuery('A002') return <div>...</div> } const Component3: React.FC = () => { const { data } = useGetUserQuery('A002',{ refetchOnMountOrArgChange: true }) return <div>...</div> } があって、Component2をマウントした後、Component3をマウントすると refetchの時と同様、Component3をマウント時(サブスクライバが追加)にデータ取得が再度行われ、その結果でComponent2のdataも書き換わる。(Component2でまたデータ取得が行われるわけではない。) refetchOnFocus これ結構すごいなって思ったやつ これがtrueになっていると、アプリケーションがフォーカスを取り戻した時にデータ取得する。 別のタブとかから戻ってきたタイミングでデータの再取得が行われる。 import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' export interface User { id: string name: string } type Users = User[] export const userApi = createApi({ baseQuery: fetchBaseQuery({ baseUrl: 'http://example.com/api/v2/' }), // APIに対するglobalな設定値。ここに書くとendpoints全てに反映される。 refetchOnFocus: true, endpoints: (builder) => ({ getUsers: builder.query<Users, void>({ query: () => `users`, }), getUser: builder.query<User, string>({ query: (userID: string) => `users/${userID}` }), }), }) export const { useGetUsersQuery,useGetUserQuery } = userApi これ(と次のrefetchOnReconnect)は別でstoreの設定変更も必要 import { configureStore } from '@reduxjs/toolkit' import { setupListeners } from '@reduxjs/toolkit/dist/query' import { userApi } from './service/user' export const store = configureStore({ reducer: { [userApi.reducerPath]: userApi.reducer, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(userApi.middleware), }); // ここを追加 setupListeners(store.dispatch) また、後述するskipがtrueの場合はデータの再取得が動かないことも注意。(refetchOnReconnectも) refetchOnReconnect ネットワークコネクションが復活したときにデータ取得する。 import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' export interface User { id: string name: string } type Users = User[] export const userApi = createApi({ baseQuery: fetchBaseQuery({ baseUrl: 'http://example.com/api/v2/' }), // APIに対するglobalな設定値。ここに書くとendpoints全てに反映される。 refetchOnReconnect: true, endpoints: (builder) => ({ getUsers: builder.query<Users, void>({ query: () => `users`, }), getUser: builder.query<User, string>({ query: (userID: string) => `users/${userID}` }), }), }) export const { useGetUsersQuery,useGetUserQuery } = userApi storeの設定変更も必要(refetchOnFocusを参照) Mutationによるキャッシュ操作 データの登録更新削除が行われたときにその操作に影響のあるデータを再取得する必要がある。(再取得しないと登録したのにデータないじゃんってなる) RTK Queryでは、cache tagというシステムを使って再データ取得を管理する。 タグを使って、あるMutationが他のエンドポイントからのクエリによって提供されたデータを無効にする意図があるかどうかを判断する。 例えば、 ユーザIDA001を取得するクエリ ユーザIDA002を取得するクエリ ユーザをすべて取得するクエリ があったとして、 ユーザIDA002の情報を更新すると上記の2,3の結果は変わるが、1の結果には影響しない。 つまり、2,3はキャッシュを無効化しデータを取得しなおすが、1のキャッシュは無効化しないようにしたい。 具体的にはクエリにprovidesTagsを設定する。 ユーザIDA001を取得するクエリ → providesTags : [{ type: 'Users', id: 'A001' }], ユーザIDA002を取得するクエリ → providesTags : [{ type: 'Users', id: 'A002' }], ユーザをすべて取得するクエリ → providesTags : [{ type: 'Users', id: 'A001' },{ type: 'Users', id: 'A002' }...] そしてMutationにinvalidatesTagsを設定する。 ユーザIDA002を更新するMutation → invalidateTags : [{ type: 'Users', id: 'A002' }], invalidateTagsを持つMutationが実行されたときに、そのタグの内容と一致するprovidesTagsを持つクエリのキャッシュを無効にし、データの再取得を行わせる。 実装としてはこんな感じ import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' export interface User { id: string name: string } type Users = User[] export const userApi = createApi({ baseQuery: fetchBaseQuery({ baseUrl: 'http://example.com/api/v2/' }), // 利用するタイプをここに書く。リソース毎にタイプを定義するんじゃないだろうか。 tagTypes: ['Users'], endpoints: (builder) => ({ getUsers: builder.query<Users, void>({ query: () => `users`, providesTags: (result) => result ? [ ...result.map(({ id }) => ({ type: 'Users', id } as const)), // addUserがあったときのために特別なタグを用意する。 { type: 'Users', id: 'LIST' }, ] : // エラーがあった場合でもユーザ追加をしたタイミングで再データ取得をする。 [{ type: 'Users', id: 'LIST' }], }), getUser: builder.query<User, string>({ query: (userID: string) => `users/${userID}`, providesTags: (result, error, id) => [{ type: 'Users', id }], }), addUser: builder.mutation<User, Partial<User>>({ query(body) { return { url: `users`, method: 'POST', body, } }, // getUsersのキャッシュを無効化する invalidatesTags: [{ type: 'Users', id: 'LIST' }], }), updateUser: builder.mutation<User, Partial<User>>({ query(data) { const { id, ...body } = data return { url: `users/${id}`, method: 'PUT', body, } }, // 更新したユーザIDを含むクエリのキャッシュのみを無効化する。 invalidatesTags: (result, error, { id }) => [{ type: 'Users', id }], }), deletePost: builder.mutation<{ success: boolean; id: string }, string>({ query(id) { return { url: `users/${id}`, method: 'DELETE', } }, // 削除したユーザIDを含むクエリのキャッシュのみを無効化する。 invalidatesTags: (result, error, id) => [{ type: 'Users', id }], }), }), }) // Mutationはuse + Mutation名 + Mutationという名前のHookができる。 export const { useGetUsersQuery,useGetUserQuery,useAddUserMutation,useUpdateUserMutation,useDeletePostMutation } = userApi 詳しいサンプルは公式のCodeSandboxがわかりやすかった(突然の丸投げ) https://codesandbox.io/s/github/reduxjs/redux-toolkit/tree/master/examples/query/react/mutations Skipを使った条件付きのデータ取得 基本的にはコンポーネントがマウントされたタイミングでクエリは自動的に実行されるが、自動で実行したくない場合もある。 クエリを自動的に実行しないためには、skipパラメータを利用する。 import React from 'react' import { useGetUserQuery } from './app/service/user' const SkipComponent: React.FC = () => { const [skip, setSkip] = React.useState(true) const { data, isUninitialized } = useGetUserQuery('A002', { skip, }) return ( <div> <div>{isUninitialized && 'データ未取得'}</div> <div>{data && data.name}</div> <button onClick={() => setSkip(false)} > データを取得する </button> </div> ); } export default SkipComponent; skipがtrueの場合はクエリが実行されない。 また、この時isUninitializedがtrueになる。 おわりに キャッシュのコントロールをうまくやったりするのにはとてもよさそう。 Reduxのthunkにつらみを覚えている人は手を出すのありかも。 個人的にはRedux Toolkitも最高って感じなので合わせて使っていきたい気持ち。 公式ドキュメントまだ半分くらいしか読んでないのでアレですがちらっと見た感じWebSocketとかとも使えたり自動生成とかもあるみたい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React: useRefで参照したinputから同じformのsubmitボタンを辿る

ReactでuseRefで参照したinputから同じformのsubmitボタンを辿って別コンポーネントからsubmitボタンをクリックするメモ ——formのref(例えばformRefという名前)を渡して、formRef.submit()すると、ページ更新されてしまうのでsubmitボタンをクリックすることにした。なにかいい方法がある気はするが、とりあえず回避策—— ComponentBからComponentAのformをSubmitする。但し渡すのはinputのrefとする。つまり、ComponentBからinputに値をセットしてからSubmitしたい。 "form" と Element ID で辿れる。 ComponentA export default function ComponentA(props) { const inputTextRef = useRef() const onSubmit = (e) => { e.preventDefault() ...何かの処理... } return ( <form onSubmit={ onSubmit }> <input type="text" ref={ inputTextRef } /> {/* <-- refを取るのはinput要素とする */} <button type="submit" id="submitButton">Submit</button>   {/* <-- idを設定 */} </form> <ComponentB inputTextRef={ inputTextRef } /> ) } ComponentB export default function ComponentB(props) { const submitWithValue = (e) => { e.preventDefault() props.inputTextRef.current.value = "Special Value" // <-- ref参照先を更新 props.inputTextRef.current.form.submitButton.click() // <-- 同じformのsubmitボタンをこんなふうにたどれる } return ( <button onClick={ submitWithValue } >Submit ComponentA with a special value</button> ) }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初学者こそ英語教材でプログラミングを学んだ方がいいかも

初学者である僕がプログラミング学習との向き合い方を再考させられる体験があったので、ポエムとして軽く書いておきます。テックな記事でないので、その点悪しからず。 僕はいま独学でReactを勉強しているのですが、公式ドキュメントだけだとどうしても厳しいし、何かいい教材は無いか、、そう思っていた時に、普段は避けていた英語圏の動画が目に留まりました。 YouTubeのこの動画です↓ なんとなく、これをやってみることにしました。 一通り終えてみて「日本語の教材より英語の方がわかりやすくね?」と思ってしまいました。ちなみに僕は英語はほぼわかんないです。中学英語ですら怪しいレベル。 じゃあなぜ、わかりやすいと思ったのか? 英語でプログラミングを学ぶことについて僕なりの発見がありました。 "日本語"でプログラミングを学ぶことの難しさ 僕たちは「プログラミングを始めるぞ!」ってなった時にはひとまず、日本語のドキュメントなり教材なりをあたりますよね。ほとんどの人がそうのはず。 当然ネイティブだからこそ全ての情報を読み取れる(聞き取れる)わけですが、初学者の段階においてはこれがかえって仇となるケースもある。そんな話です。 ■俺は"言葉"に支配されっぱなしだけど、皆はどう? 僕はプログラミング学習を進める中で、「学んだことが理解できていない」という初学者にとってある程度スルーすべき事象に対して、「正面からぶつかり過ぎてしまう」ことが非常に多いです。 というのも僕は何かを学ぶ際、言葉の意味を理解し納得できるまで立ち止まってしまうという癖があります。その割には理解力が低いのでいつまでたっても学習が進まない。当然手を動かす時間も減ってしまうわけです。(?ワーキングメモリが低い!) とくにプログラミングの”概念”を学ぶ際に、「なぜそうなるのか」「これにはどういう意味があるのか」みたいなことをいちいち深掘りしていると、本当に疲弊してしまいます。(多分、初学者が一番考えてはいけないことです) そして言葉の意味や定義に支配されてしまうあまり、 手を動かすことがおざなりになってしまう、あるいは手を動かすことを辞めてしまう こういった悪循環に陥ることが良くあります。 ■「とにかく手を動かすべき」なのは分かってるつもりだけど... プログラミングの学習というのはある種の説明地獄です。 だからこそ、完全に理解できてなくてもいいからまずは一度手を動かして何か完成させる のが上達の近道だと言われているのだと思います。 つまりは、一度フカン視点で全体感を掴むことが大切。 わかっちゃいるんですけどね。。 僕は、説明されたものはできるだけクリティカルに理解しなければいけない気がする。そうでなければ先に進んだことにはならないのではないか、こういう病的な強迫観念に襲われてしまう。 分からないことに対して能動的になれるのは良いことだと思うのですが、僕の場合は少しやり過ぎ、考え過ぎの傾向が強いです。 まあ、こういう人もいるわけです。 英語で学習してみて良かったと思うこと 冒頭で述べた通り、YouTubeに転がっている英語のReactチュートリアル動画をやりました。ここからはその感想です。 ■とにかく手を動かすことに専念できた だって何言ってるかほとんど分からないんだもの。(笑) しかし見まねで一緒に手を動かしていると、「何をしようとしているのか」がふんわりと掴めることに気づきました。(Reactに関しては若干の前知識はあった) たとえば「props」というものが出てきた時 「props」ってなんだろう?とかいちいち反応する必要はなくて、何回も書いているとさすがになんとな~く役割がわかるようになってきます。 その時点で「props」をクリティカルに理解している必要はないし、そもそもそう思うこと自体が初学者の傲慢でしかないという風にすら思いました。 少なくともビギナーの段階においては、 ・ほぼ何言ってるか分かんねえけど、ところどころ知ってる単語や表現があるから何となくわかる ・黙って書け、雰囲気を味わえ こういった Don't think, feel. な環境のほうが健全なのではないか? そしてなにより、曲がりなりにも自分で書いたコードが動くのは気持ちいい!!ということも改めて実感しました。 ■ちょうどいい感じに能動的になれる 「手を動かしていく中で何回も出てくるけど、いまいちわからないな~」と思ったところだけ都度ググるということをやりました。逆にいちいち調べずにスルーしているところもたくさんある。でも今はそれでいいんです。このくらいの情報密度でいい。 何より、自分で書いて動かした後に意味を調べるほうが圧倒的に理解は早まるということを体感しました。 ■英語アレルギーも改善される(多少はね?) そもそもほとんどのプログラミング言語は英語主体で作られているわけなので、英語がきちんと読める、聴けるに越したことはないです。 英語のレクチャー動画を見るだけで英語がマスターできるわけではありませんが、英語圏の"言い回しやノリ"みたいなものを肌で感じることはできます。これはプログラミングの学習に限らず結構でかいメリットだと思います。 ■日本語での補完は必須 英語教材でのプログラミング学習はあまり考えずに実践に集中できるので、全体感を掴むのにはうってつけです。ただし、細かい部分はさすがにわかんないままです。 なので、ある程度手を動かしてその言語やフレームワークの雑感を掴んだら、細かい部分は日本語のリソースで補完していく。こういう感じで学習を進めていくのがいいんじゃないかと思います。 話をまとめる この記事で言いたいことをまとめると、 物事を一度に全部理解しようとするのはある種の傲慢 プログラミング学習は日本語にこだわらなくていい 言葉に支配されたら終わり ようつべでよく見る英語のプログラミング動画はガンガン使うべき こんな感じです。 僕みたいに不器用な思考回路の持ち主も中々いない思いますが、もし共感する部分があれば是非YouTubeに転がっている英語のプログラミングレクチャーを試してみてください。 言葉が全部分からない方が良いこともあるんです。 また、お前の言ってることおかしくね?などなど思うことがあれば是非コメントにお寄せください。 本質的には言語の問題ではなく、筆者の性質・理解力に問題があるんじゃね? ~それでは~
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初学者こそ英語教材でプログラミングを学んだ方がいいかも

初学者である僕がプログラミング学習との向き合い方を再考させられる体験があったので、ポエムとして軽く書いておきます。テックな記事でないので、その点悪しからず。 僕はいま独学でReactを勉強しているのですが、公式ドキュメントだけだとどうしても厳しいし、何かいい教材は無いか、、そう思っていた時に、普段は避けていた英語圏の動画が目に留まりました。 YouTubeのこの動画です↓ なんとなく、これをやってみることにしました。 一通り終えてみて「日本語の教材より英語の方がわかりやすくね?」と思ってしまいました。ちなみに僕は英語はほぼわかんないです。中学英語ですら怪しいレベル。 じゃあなぜ、わかりやすいと思ったのか? 英語でプログラミングを学ぶことについて僕なりの発見がありました。 "日本語"でプログラミングを学ぶことの難しさ 僕たちは「プログラミングを始めるぞ!」ってなった時にはひとまず、日本語のドキュメントなり教材なりをあたりますよね。ほとんどの人がそうのはず。 当然ネイティブだからこそ全ての情報を読み取れる(聞き取れる)わけですが、初学者の段階においてはこれがかえって仇となるケースもある。そんな話です。 ■俺は"言葉"に支配されっぱなしだけど、皆はどう? 僕はプログラミング学習を進める中で、「学んだことが理解できていない」という初学者にとってある程度スルーすべき事象に対して、「正面からぶつかり過ぎてしまう」ことが非常に多いです。 というのも僕は何かを学ぶ際、言葉の意味を理解し納得できるまで立ち止まってしまうという癖があります。その割には理解力が低いのでいつまでたっても学習が進まない。当然手を動かす時間も減ってしまうわけです。(?ワーキングメモリが低い!) とくにプログラミングの”概念”を学ぶ際に、「なぜそうなるのか」「これにはどういう意味があるのか」みたいなことをいちいち深掘りしていると、本当に疲弊してしまいます。(多分、初学者が一番考えてはいけないことです) そして言葉の意味や定義に支配されてしまうあまり、 手を動かすことがおざなりになってしまう、あるいは手を動かすことを辞めてしまう こういった悪循環に陥ることが良くあります。 ■「とにかく手を動かすべき」なのは分かってるつもりだけど... プログラミングの学習というのはある種の説明地獄です。 だからこそ、完全に理解できてなくてもいいからまずは一度手を動かして何か完成させる のが上達の近道だと言われているのだと思います。 つまりは、一度フカン視点で全体感を掴むことが大切。 わかっちゃいるんですけどね。。 僕は、説明されたものはできるだけクリティカルに理解しなければいけない気がする。そうでなければ先に進んだことにはならないのではないか、こういう病的な強迫観念に襲われてしまう。 分からないことに対して能動的になれるのは良いことだと思うのですが、僕の場合は少しやり過ぎ、考え過ぎの傾向が強いです。 まあ、こういう人もいるわけです。 英語で学習してみて良かったと思うこと 冒頭で述べた通り、YouTubeに転がっている英語のReactチュートリアル動画をやりました。ここからはその感想です。 ■とにかく手を動かすことに専念できた だって何言ってるかほとんど分からないんだもの。(笑) しかし見まねで一緒に手を動かしていると、「何をしようとしているのか」がふんわりと掴めることに気づきました。(Reactに関しては若干の前知識はあった) たとえば「props」というものが出てきた時 「props」ってなんだろう?とかいちいち反応する必要はなくて、何回も書いているとさすがになんとな~く役割がわかるようになってきます。 その時点で「props」をクリティカルに理解している必要はないし、そもそもそう思うこと自体が初学者の傲慢でしかないという風にすら思いました。 少なくともビギナーの段階においては、 ・ほぼ何言ってるか分かんねえけど、ところどころ知ってる単語や表現があるから何となくわかる ・黙って書け、雰囲気を味わえ こういった Don't think, feel. な環境のほうが健全なのではないか? そしてなにより、曲がりなりにも自分で書いたコードが動くのは気持ちいい!!ということも改めて実感しました。 ■ちょうどいい感じに能動的になれる 「手を動かしていく中で何回も出てくるけど、いまいちわからないな~」と思ったところだけ都度ググるということをやりました。逆にいちいち調べずにスルーしているところもたくさんある。でも今はそれでいいんです。このくらいの情報密度でいい。 何より、自分で書いて動かした後に意味を調べるほうが圧倒的に理解は早まるということを体感しました。 ■英語アレルギーも改善される(多少はね?) そもそもほとんどのプログラミング言語は英語主体で作られているわけなので、英語がきちんと読める、聴けるに越したことはないです。 英語のレクチャー動画を見るだけで英語がマスターできるわけではありませんが、英語圏の"言い回しやノリ"みたいなものを肌で感じることはできます。これはプログラミングの学習に限らず結構でかいメリットだと思います。 ■日本語での補完は必須 英語教材でのプログラミング学習はあまり考えずに実践に集中できるので、全体感を掴むのにはうってつけです。ただし、細かい部分はさすがにわかんないままです。 なので、ある程度手を動かしてその言語やフレームワークの雑感を掴んだら、細かい部分は日本語のリソースで補完していく。こういう感じで学習を進めていくのがいいんじゃないかと思います。 話をまとめる この記事で言いたいことをまとめると、 物事を一度に全部理解しようとするのはある種の傲慢 プログラミング学習は日本語にこだわらなくていい 言葉に支配されたら終わり ようつべでよく見る英語のプログラミング動画はガンガン使うべき こんな感じです。 僕みたいに不器用な思考回路の持ち主も中々いない思いますが、もし共感する部分があれば是非YouTubeに転がっている英語のプログラミングレクチャーを試してみてください。 言葉が全部分からない方が良いこともあるんです。 また、お前の言ってることおかしくね?などなど思うことがあれば是非コメントにお寄せください。 本質的には言語の問題ではなく、筆者の性質・理解力に問題があるんじゃね? ~それでは~
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AmplifyにてLambdaへAPIコールするときにリクエストパラメータが受け取れない

結論 GETリクエストするときは、リクエストボディではなくクエリストリングかパスパラメータを使いましょう。 ハマったこと クライアント側はこのように呼び出し(ちなみにReactです) apiCall.tsx import { API } from "aws-amplify"; export const hoge = async ( hogeId: string ): Promise<void> => { const apiName = "hogeAPI"; const path = "/"; const params = { body: { hogeId: hogeId, }, }; return await API.get(apiName, path, params); }; Lambda側ではこのように受け取り lambda.js exports.handler = async (event) => { const parameter = JSON.parse(event.body); const hogeId = parameter.hogeId // あとは割愛 } ただこれでAPI呼び出すと502。CloudWatchログを見てみると、bodyがnull。 GETで呼び出しするのにbody指定なのが良くない。 正しくはこう。 apiCall.tsx import { API } from "aws-amplify"; export const hoge = async ( hogeId: string ): Promise<void> => { const apiName = "hogeAPI"; const path = "/?hogeId=" + hogeId; const params = {}; return await API.get(apiName, path, params); }; Lambda側は下記。 このとき渡ってくるqueryStringParametersの中身を見ると、一見JSON文字列のように見えて、値の部分がダブルクオートで囲まれていないため、このままJSON.parseするとエラーになってしまう。 一度JSON.stringifyするといい。 lambda.js exports.handler = async (event) => { const parameter = JSON.parse(JSON.stringify(event.queryStringParameters)); const hogeId = parameter.hogeId // あとは割愛 } これで呼び出し成功。 GETでリクエストボディ指定しちゃダメなのは基本ですが、よく忘れてハマってしまうため備忘録メモ。 蛇足だがReactでAPIコールするときはこんな感じで呼び出せばいい。 client.tsx import hoge from "path/to/apiCall"; hoge("hogeId") .then((res) => { console.log("success"); }) .catch((e) => { console.error(e); }); もっといい書き方がある気がしているので、あくまで参考程度でお願いします。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GETリクエストするときは、リクエストボディを使わないでね

結論 GETリクエストするときは、リクエストボディではなくクエリストリングかパスパラメータを使いましょう。(掲題どおり) ハマったこと クライアント側はこのように呼び出し(ちなみにReactでAmplifyを使っています) apiCall.tsx import { API } from "aws-amplify"; export const hoge = async ( hogeId: string ): Promise<void> => { const apiName = "hogeAPI"; const path = "/"; const params = { body: { hogeId: hogeId, }, }; return await API.get(apiName, path, params); }; APIはLambdaを使います。Lambda側ではこのように受け取り lambda.js exports.handler = async (event) => { const parameter = JSON.parse(event.body); const hogeId = parameter.hogeId // あとは割愛 } ただこれでAPI呼び出すと502。CloudWatchログを見てみると、bodyがnull。 GETで呼び出しするのにbody指定なのが良くない。 正しくはこう。 apiCall.tsx import { API } from "aws-amplify"; export const hoge = async ( hogeId: string ): Promise<void> => { const apiName = "hogeAPI"; const path = "/?hogeId=" + hogeId; const params = {}; return await API.get(apiName, path, params); }; Lambda側は下記。 このとき渡ってくるqueryStringParametersの中身を見ると、一見JSON文字列のように見えて、値の部分がダブルクオートで囲まれていないため、このままJSON.parseするとエラーになってしまう。 一度JSON.stringifyするといい。 lambda.js exports.handler = async (event) => { const parameter = JSON.parse(JSON.stringify(event.queryStringParameters)); const hogeId = parameter.hogeId // あとは割愛 } これで呼び出し成功。 GETでリクエストボディ指定しちゃダメなのは基本ですが、よく忘れてハマってしまうため備忘録メモ。 蛇足だがReactでAPIコールするときはこんな感じで呼び出せばいい。 client.tsx import hoge from "path/to/apiCall"; hoge("hogeId") .then((res) => { console.log("success"); }) .catch((e) => { console.error(e); }); もっといい書き方がある気がしているので、あくまで参考程度でお願いします。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React/React Hook Form/react-beautiful-dnd】ReactでFormのパーツをドラッグ&ドロップさせる

はじめに React Hook Form で作成したフォーム内のinputをドラッグ&ドロップさせる機能を作ったのでアウトプット。 使用ライブラリ React 17.0.1 React Hook Form ^7.0.0 react-beautiful-dnd 13.1.1 いきなりどうでもいいことかもしれませんが drag and drop を dadではなくdndで訳すのは andのAを発音しないパターンのやつだからぽいです。 ロックンロール(Rock and Roll)と同じ考え方?という解釈 参照記事: https://kenjimorita.jp/why-draganddrop-dnd/ ライブラリ選定について ライブラリ選定において、軽く調べたものをzennにscrapにしてます。(ほんとに軽くしか調べていない) https://zenn.dev/yuto_nakamoto/scraps/b9793f51c65bec 実装 formのコード 同じようなInputをdata分 mapを使って表示させます。 今回は仮データにしています。本来はapiで取得したデータなどを差し込んで使用したりイメージだといいかもしれません。 Form.tsx import React from "react"; import { useForm } from "react-hook-form"; import { Input } from "./Input" interface UseFormInputs { mailAddress: string password: string } export default function Form() { // 今回は仮データ、本来はapiで取得したデータなどを差し込んで使用したりする。 const formdata = [{id: 1, value:'test'},{id: 2, value:'test2'},{id: 3, value:'test3'}] const [data, setData] = useState(formdata) const methods = useForm<UseFormInputs>(); const { register, handleSubmit, reset } = methods const onSubmit = (data: UseFormInputs) => { console.log('test') }; return ( <div> <h1>リセット試す用のフォームです</h1> <form onSubmit={handleSubmit(onSubmit)}> { data.map((item, index) => { return (<Input data={item} index={index} methods={methods}/>) }) } <input type="submit" /> </form> </div> ); } Input.tsx export const Input = (props) => { return( <input type="text" {...props.methods.register(`text[${props.index}]`)} defaultValue={props.data.value} /> } ) DragAndDrop実装 react-beautiful-dndを使って実装する <DragDropContext /> - ドラッグ&ドロップを有効にするアプリケーションの部分をラップするドM <Droppable /> - ドロップできるエリア 中にDraggbleDomが必要 <Draggable /> - ドラッグできる対象のもの DragAndDrop.tsx import React from 'react' import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd' import { dragAndDropSetValue } from '../../../utils/dragAndDropSetValue' export const DragAndDrop = (props) => { const { getValues, setValue } = props.methods // ドラッグ&ドロップ時の挙動 function handleOnDragEnd(result: any) { const items = Array.from(props.data); const [reorderedItem] = items.splice(result.source.index, 1); items.splice(result.destination.index, 0, reorderedItem); props.setData(items); dragAndDropSetValue(result.destination.index, result.source.index, getValues, setValue) } // ドラッグ&ドロップ対象にpropsが必要なのでcloneElementでなんとかしている。 const DragAndDropItem = (props) => { const { children, ...newProps } = props; const childrenWithProps = React.Children.map(children, (child: React.ReactElement) => React.cloneElement(child, { ...newProps })); return ( <div key={props.index} ref={props.provided.innerRef} {...props.provided.draggableProps} {...props.provided.dragHandleProps}> {childrenWithProps} </div> ) } return ( props.data && props.data.length !== 0 ? <div> <DragDropContext onDragEnd={handleOnDragEnd}> <Droppable droppableId="characters"> {(provided) => ( <div {...provided.droppableProps} ref={provided.innerRef}> {props.data.map((item, index) => { return ( <Draggable key={item.id} draggableId={`${item.id}`} index={index}> {(provided) => ( <DragAndDropItem index={index} provided={provided} item={item} methods={props.methods}> {props.children} </DragAndDropItem> )} </Draggable> ) })} {provided.placeholder} </div> )} </Droppable> </DragDropContext> </div> : null ) } DragAndDrop時にformの値を変更させる関数を用意する こちらはタイトル通りDragAndDrop時にformの値を変更させる関数を用意しました。 すべてのformの値が変わると重い処理になってしまうので、DragAndDrop時に関連するformの値のみ変更するように条件分岐を追加しています。 例) 1-2-3 が 1-3-2 になったとき 1は値を変更する必要がないので2と3だけ処理をするようにしています。 dragAndDropSetValue.tsx import { UseFormSetValue } from "react-hook-form" // destinationIndex: 移動先, sourceIndex: 移動元 export const dragAndDropSetValue = (destinationIndex: number, sourceIndex: number, getValues, setValue) => { const formValue = getValues('text') if (formValue === undefined) return if (destinationIndex < sourceIndex) { for (let i: number = destinationIndex; i <= sourceIndex; i++) { const settingValue = i === destinationIndex ? formValue[sourceIndex] : formValue[i - 1] const setValueTarget = `text[${i}]` setValue(setValueTarget, settingValue) } } else if (sourceIndex < destinationIndex) { for (let i = sourceIndex; i <= destinationIndex; i++) { const settingValue = i === destinationIndex ? formValue[sourceIndex] : formValue[i + 1] const setValueTarget = `text[${i}]` setValue(setValueTarget, settingValue) } } } form部分にドラッグ&ドロップ用のコンポーネントを組み込む ドラッグ&ドロップ用のコンポーネントを作成したので、それを組み込んでいきます。 もともとInputにpropsを渡していたのですが、こちらをなくしています。 ですがInputへは、DragAndDropコンポーネント側からpropsを付与しています。 渡し方については別の記事で書いていますのでそちらを参考にしてみてください。 https://qiita.com/nakamo-03/items/abf2634c2c3b068e0220 Form.tsx import React from "react"; import { useForm } from "react-hook-form"; interface UseFormInputs { mailAddress: string password: string } export default function Form() { // 今回は仮データ、本来はapiで取得したデータなどを差し込んで使用したりする。 const formdata = [{id: 1, value:'test'},{id: 2, value:'test2'},{id: 3, value:'test3'}] const [data, setData] = useState(formdata) const { register, handleSubmit, reset } = useForm<UseFormInputs>(); const onSubmit = (data: UseFormInputs) => { console.log('test') }; return ( <div> <h1>リセット試す用のフォームです</h1> <form onSubmit={handleSubmit(onSubmit)}>      <DragAndDrop data={array} setData={setData} methods={methods} > <Input/> </DragAndDrop> <input type="submit" /> </form> </div> ); } 終わりに ドラッグ&ドロップとformを組み合わせると、結構レンダリング回数が増えてしまうのでstateの管理や、formの値の変更は慎重に行う必要があることがわかりました。 あと、ちょっと雑な部分がある+実際に動く画面みた方がいい箇所もあったりするかもなので追記がんばります。 参考 React Hook Form ドキュメント なぜDragAndDrop(ドラッグアンドドロップ)を「DnD」というの?なぜ「DaD」じゃないの react-beautiful-dnd を使ってドラッグ&ドロップ機能を実装する Github: react-beautiful-dnd
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReactでFormのパーツをDragAndDropさせる

はじめに React Hook Form で作成したフォーム内のinputをDragAndDropさせる機能を作ったのでアウトプット。 使用ライブラリ React 17.0.1 React Hook Form ^7.0.0 react-beautiful-dnd 13.1.1 いきなりどうでもいいことかもしれませんが drag and drop を dadではなくdndで訳すのは andのAを発音しないパターンのやつだからぽいです。 ロックンロール(Rokin and Role)と同じ考え方?という解釈 参照記事: https://kenjimorita.jp/why-draganddrop-dnd/ ライブラリ選定について ライブラリ選定において、軽く調べたものをzennにscrapにしてます。(ほんとに軽くしか調べていない) https://zenn.dev/yuto_nakamoto/scraps/b9793f51c65bec 実装 formのコード 同じようなInputをdata分 mapを使って表示させます。 今回は仮データにしています。本来はapiで取得したデータなどを差し込んで使用したりイメージだといいかもしれません。 Form.tsx import React from "react"; import { useForm } from "react-hook-form"; import { Input } from "./Input" interface UseFormInputs { mailAddress: string password: string } export default function Form() { // 今回は仮データ、本来はapiで取得したデータなどを差し込んで使用したりする。 const formdata = [{id: 1, value:'test'},{id: 2, value:'test2'},{id: 3, value:'test3'}] const [data, setData] = useState(formdata) const methods = useForm<UseFormInputs>(); const { register, handleSubmit, reset } = methods const onSubmit = (data: UseFormInputs) => { console.log('test') }; return ( <div> <h1>リセット試す用のフォームです</h1> <form onSubmit={handleSubmit(onSubmit)}> { data.map((item, index) => { return (<Input data={item} index={index} methods={methods}/>) }) } <input type="submit" /> </form> </div> ); } Input.tsx export const Input = (props) => { return( <input type="text" {...props.methods.register(`text[${props.index}]`)} defaultValue={props.data.value} /> } ) DragAndDrop実装 react-beautiful-dndを使って実装する <DragDropContext /> - ドラッグアンドドロップを有効にするアプリケーションの部分をラップするドM <Droppable /> - ドロップできるエリア 中にDraggbleDomが必要 <Draggable /> - ドラッグできる対象のもの DragAndDrop.tsx import React from 'react' import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd' import { dragAndDropDispatch } from '../../../utils/dragAndDropDispatch' export const DragAndDrop = (props) => { const { getValues, setValue } = props.methods // ドラッグ&ドロップ時の挙動 function handleOnDragEnd(result: any) { const items = Array.from(props.data); const [reorderedItem] = items.splice(result.source.index, 1); items.splice(result.destination.index, 0, reorderedItem); props.setData(items); dragAndDropDispatch(result.destination.index, result.source.index, getValues, setValue) } // ドラッグ&ドロップ対象にpropsが必要なのでcloneElementでなんとかしている。 const DragAndDropItem = (props) => { const { children, ...newProps } = props; const childrenWithProps = React.Children.map(children, (child: React.ReactElement) => React.cloneElement(child, { ...newProps })); return ( <div key={props.index} ref={props.provided.innerRef} {...props.provided.draggableProps} {...props.provided.dragHandleProps}> {childrenWithProps} </div> ) } return ( props.data && props.data.length !== 0 ? <div> <DragDropContext onDragEnd={handleOnDragEnd}> <Droppable droppableId="characters"> {(provided) => ( <div {...provided.droppableProps} ref={provided.innerRef}> {props.data.map((item, index) => { return ( <Draggable key={item.id} draggableId={`${item.id}`} index={index}> {(provided) => ( <DragAndDropItem index={index} provided={provided} item={item} methods={props.methods}> {props.children} </DragAndDropItem> )} </Draggable> ) })} {provided.placeholder} </div> )} </Droppable> </DragDropContext> </div> : null ) } DragAndDrop時にformの値を変更させる関数を用意する こちらはタイトル通りDragAndDrop時にformの値を変更させる関数を用意しました。 すべてのformの値が変わると重い処理になってしまうので、DragAndDrop時に関連するformの値のみ変更するように条件分岐を追加しています。 例) 1-2-3 が 1-3-2 になったとき 1は値を変更する必要がないので2と3だけ処理をするようにしています。 dragAndDropDispatch.tsx import { UseFormSetValue } from "react-hook-form" // destinationIndex: 移動先, sourceIndex: 移動元 export const dragAndDropDispatch = (destinationIndex: number, sourceIndex: number, getValues, setValue) => { const formValue = getValues('text') if (formValue === undefined) return if (destinationIndex < sourceIndex) { for (let i: number = destinationIndex; i <= sourceIndex; i++) { const settingValue = i === destinationIndex ? formValue[sourceIndex] : formValue[i - 1] const setValueTarget = `text[${i}]` setValue(setValueTarget, settingValue) } } else if (sourceIndex < destinationIndex) { for (let i = sourceIndex; i <= destinationIndex; i++) { const settingValue = i === destinationIndex ? formValue[sourceIndex] : formValue[i + 1] const setValueTarget = `text[${i}]` setValue(setValueTarget, settingValue) } } } form部分にDragAndDrop用のコンポーネントを組み込む DragAndDrop用のコンポーネントを作成したので、それを組み込んでいきます。 もともとInputにpropsを渡していたのですが、こちらをなくしています。 ですがInputへは、DragAndDropコンポーネント側からpropsを付与しています。 渡し方については別の記事で書いていますのでそちらを参考にしてみてください。 https://qiita.com/nakamo-03/items/abf2634c2c3b068e0220 Form.tsx import React from "react"; import { useForm } from "react-hook-form"; interface UseFormInputs { mailAddress: string password: string } export default function Form() { // 今回は仮データ、本来はapiで取得したデータなどを差し込んで使用したりする。 const formdata = [{id: 1, value:'test'},{id: 2, value:'test2'},{id: 3, value:'test3'}] const [data, setData] = useState(formdata) const { register, handleSubmit, reset } = useForm<UseFormInputs>(); const onSubmit = (data: UseFormInputs) => { console.log('test') }; return ( <div> <h1>リセット試す用のフォームです</h1> <form onSubmit={handleSubmit(onSubmit)}>      <DragAndDrop data={array} setData={setData} methods={methods} > <Input/> </DragAndDrop> <input type="submit" /> </form> </div> ); } 終わりに DragAndDropとformを組み合わせると、結構レンダリング回数が増えてしまうのでstateの管理や、formの値の変更は慎重に行う必要があることがわかりました。 あと、ちょっと雑な部分がある+実際に動く画面みた方がいい箇所もあったりするかもなので追記がんばります。 参考 React Hook Form ドキュメント なぜDragAndDrop(ドラッグアンドドロップ)を「DnD」というの?なぜ「DaD」じゃないの react-beautiful-dnd を使ってドラッグ&ドロップ機能を実装する Github: react-beautiful-dnd
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む