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

Typescript + React 環境構築

create-react-appを使わずにReactの環境構築 まっさらの状態からReactを使ってブラウザで「Hello World」を表示する ※npmではなくyarn なのでyarn initでlockファイルを作成 Webpack yarn add -D webpack webpack-cli webpack-dev-server html-webpack-plugin babel yarn add -D @babel/core @babel/runtime @babel/plugin-transform-runtime @babel/preset-env babel-loader React yarn add react react-dom yarn add -D @babel/preset-react Typescript yarn add -D typescript @babel/preset-typescript @types/react @types/react-dom tsconfig.jsonの作成 yarn run tsc --init { "compilerOptions": { "typeRoots": ["types", "node_modules/@types"],//pngの型がないとエラーが起きるので後付け "jsx": "react", "target": "ES2021", "lib": [ "ES2021", "dom" ] , /* Modules */ "module": "commonjs", "moduleResolution": "node", "resolveJsonModule": true , /* Emit */ "outDir": "./dist", "noEmit": true, "downlevelIteration": true, /* Interop Constraints */ "isolatedModules": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true , /* Type Checking */ "strict": true, "noImplicitAny": true , "strictNullChecks": true , "strictFunctionTypes": true , "strictBindCallApply": true , "strictPropertyInitialization": true , "noImplicitThis": true , "useUnknownInCatchVariables": true , "alwaysStrict": true , "noUnusedLocals": true , "noUnusedParameters": true , "exactOptionalPropertyTypes": true , "noImplicitReturns": true , "noFallthroughCasesInSwitch": true , "noUncheckedIndexedAccess": true , "noImplicitOverride": true , "noPropertyAccessFromIndexSignature": true , "allowUnusedLabels": true , "allowUnreachableCode": true }, "exclude": ["node_modules"], "include": ["src/**/*"] } png型エラーしないように自作 src > typesフォルダ > index.d.ts declare module "*.png" declare module "*.jpeg" webpack.config.jsの作成 const path = require("path") const HtmlWebpackPlugin = require("html-webpack-plugin") module.exports = { mode: "development", entry: path.resolve(__dirname, "./src/index.tsx"), // 始まるところ output: { path: path.resolve(__dirname, "dist"), // distを本番環境 filename: "index.js",// jsに変換 }, resolve: { modules: [path.resolve(__dirname, "node_modules")], extensions: [".ts", ".tsx", ".js"], }, module: { rules: [ { test: [/\.ts$/, /\.tsx$/], use: [ { loader: "babel-loader", options: { presets: ["@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript"],         plugins: ["@babel/plugin-transform-runtime"],// fetchを使いたかったができなかったため後から追加した }, }, ], }, { test: /\.png/, type: "asset/resource", }, ], }, plugins: [ new HtmlWebpackPlugin({ template: path.resolve(__dirname, "./src/index.html"), }), ], devServer: { port: 8111,// サーバー側とPORTが被らないように指定 static: { directory: path.join(__dirname, "dist"), }, }, } index.htmlの作成 <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>タイトル</title> </head> <body> <div id="root"></div> </body> </html> index.tsxの作成 import React from "react" import ReactDOM from "react-dom" import App from "./app" ReactDOM.render( <React.StrictMode> <App /> </React.StrictMode>, document.getElementById("root"), ) app.tsx import React from "react" const App = () => <h1>Hello World</h1> export default App 最後に package.jsonのscriptsに"start": "npx webpack serve --config webpack.config.js" を書き込む yarn start ブラウザでhttp://localhost:8111にアクセスする Hello Worldが表示される! 参考記事
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TypeError: MiniCssExtractPlugin is not a constructorの解消方法

create-react-appで作成したTypescriptプロジェクトをnpm run buildした際に、 エラーが出て困った時に解消した方法です。 エラーの内容 TypeError: MiniCssExtractPlugin is not a constructor 原因 mini-css-extract-plugin がバージョン2.5.0にアップデートされたことでエラーが起きたようです。 解消方法 今回は、mini-css-extract-pluginをバージョンダウンすることで解消できました! 同じエラーで困ってる方がいたら試してみてください! npm i -D --save-exact mini-css-extract-plugin@2.4.5 参考にしたサイト
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[antd] TableのColumnやColumnGroupは(現在)ただの印である

この記事は2022年1月現在なので、将来変わるかもしれません TableのColumnやColumnGroup Ant DesignのTableを使う際に、ColumnやColumnGroupを使いますが、 Table - Ant Design Since this is just a syntax sugar for the prop columns, you can't compose Column and ColumnGroup with other Components. jsxで記述する Column や ColumnGroupはわかりやすさのために中身がない糖衣構文的なコンポーネントである。 つまり、ただの印であり実装上の意味を持ちません。 (そもそも昔はjsxで記述するものではなくオブジェクトで記述するものだったため?) 実装 https://github.com/ant-design/ant-design/blob/master/components/table/Column.tsx 例えば公式サンプルのこの例 https://codesandbox.io/s/yqy7e ですが、 すべて div タグに書き換えても同じように動きます。 理由 Ant Designというより、さらに依存している react-component/table の仕様にはなるのですが、 https://github.com/react-component/table/blob/master/src/hooks/useColumns.tsx#L22 ここの部分で key と props だけ取り出していることがわかります。 コンポーネント名を活かすならtypeなども活用されるはずです。 つまり、jsxのpropsやchildren(子要素)があるかどうかだけに影響されるのです。 できないこと 別にそういうこともあるよねくらいの気持ちでも良いのですが、 これでできないことは列に対するカスタムコンポーネントが作れなくなります。 実用上だと列を動的に生成したいなどでこのようにすることもあると思います。 export const CustomColomn = () => { return <Column title="Age" dataIndex="age" key="age" /> } のように作ってjsxで使っても、 何も表示されなくなります。 忘れやすいですがカスタムコンポーネントは、処理の分離だけできるというわけでなく 実装上は もちろん CustomColomn というコンポーネントを1段階挟んでいて propsなどは空になっているのです。 ちなみに、 <Table dataSource={data}> {CustomColomn()} </Table>, のように関数呼び出しにすれば大丈夫ではありますが、これが良いかどうかは..皆さんのご判断で propsも設定すると大丈夫にはなりますが、 カスタムコンポーネントを使って分離する意義が少し薄れます。 <Table dataSource={data}> <CustomColumn title="Age" dataIndex="age" key="age" /> </Table>, mapを使って動的に列の数も増やすという場合 こういうコンポーネントを使っても何も出ないということになります。 export const CustomColomns = () => { return [1, 2, 3].map((v) => <Column title={"param" + v} />); }; なので動的に列を増やしたいときは Table直下にコンポーネントを経由せずにmapを書かないといけないと思います。 <Table dataSource={data}> {[1, 2, 3].map((v) => ( <Column title={"param" + v} /> ))} </Table>, まとめ Columnや ColumnGroupは、実装上は意味を持たないものである。 ですが、将来仕様が変わるかもしれないのでもちろんこれらを使うべきである。 処理の分離するときにカスタムコンポーネントをよく使うが、1段挟まれるので、 等価ではなく意図しない挙動になるかもしれないという考慮には入れておく。 (おそらく大抵は大丈夫だと思うが) このコンポーネント以外にもこういうのはあるかもしれないです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

We no longer support global installation of Create React App 解決

エラーメッセージ npx create-react-app を使いたい時に以下のエラーメッセージが表示さば、この記事の手順に従ってください。 We no longer support global installation of Create React App. Please remove any global installs with one of the following commands: - npm uninstall -g create-react-app - yarn global remove create-react-app アンインストール npm uninstall -g create-react-appを実行してましょう。 次はnpxキャッシュをクリアしましょう:npx clear-npx-cache. 問題が解決しました これでnpx create-react-app <my-app>は実行出来ます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React-Helmet のソースコードを読んでみる

React Helmet のソースコードを読んでみたので、そのメモ。 間違い等がありましたら、ご指摘お願いします。 React Helmet とは? React で ヘッドタグを操作したい時に使う ライブラリ。 使い方は、特定のコンポーネントで、 import {Helmet} from "react-helmet" // 省略 const hoge = () => { return( <Helmet> <title>hoge</title> <meta name="description" content="hoge"/> </Helmet> // 省略 ) } を加えるだけ。これで、 の中の や が head タグ内に加わります。 では、読んでみましょう! React Helmet のコード こちらのソースコード を見ても分かるように、src フォルダ には たったの3つしかファイルはありません。 量だけなら簡単に読めそうですね。 まずは、Helmet.js から読んでみましょう。 Helmet.js import React from "react"; import PropTypes from "prop-types"; import withSideEffect from "react-side-effect"; // 省略 const Helmet = Component => class HelmetWrapper extends React.Component { // 省略 render() { const {children, ...props} = this.props; let newProps = {...props}; if (children) { newProps = this.mapChildrenToProps(children, newProps); } return <Component {...newProps} />; } const NullComponent = () => null; const HelmetSideEffects = withSideEffect( reducePropsToState, handleClientStateChange, mapStateOnServer )(NullComponent); const HelmetExport = Helmet(HelmetSideEffects); const Helmet = Component => class HelmetWrapper extends React.Component という変わった書き方になっていますが、これは Higher-order Components というものになり、HelmetSideEffects の機能を使うための実装になります。 この HelmetSideEffects の中身の withSideEffect のソースコードはこちらにありますが、Helmet のコードで 簡単に言えば、 1:reducePropsToState で 受け取った props を state に reduce し、 2:reducePropsToState で作った state (withSideEffect内部のみで分かる形) を受け取り、クライアントが動いているなら(Dom が使えるなら = canuseDom) handleClientStateChange を、サーバーで動いているなら (Dom が使えないなら) mapStateOnServer を実行し、state を view に反映させるなどしています。 ここで出てきた reducePropsToState と handleClientStateChange と mapStateOnServer が書いてあるファイルが、HelmetUtils.js になります。 しかし withSideEffect で最初に実行される reducePropsToState を動かすには、<Helmet></Helmet> の中にある要素 ( Children ) を Props にしてコンポーネントに渡す必要があります。 この Children を Props に渡す動作をしているのが、Helmet.js です。 内容は割愛しますが、上のコードの Helmet.js if (children) { newProps = this.mapChildrenToProps(children, newProps); } 部分の this.mapChildrenToProps(children, newProps) が、children と props を結合させている実装です。 その後、children が Props と結合した後で 呼び出されるのが、reducePropsToState と handleClientStateChange (とサーバーサイドなら mapStateOnServer) です。 まずは、受け取った Props を state に変換する reducePropsToState を見てみましょう。 HelmetUtils.js const reducePropsToState = propsList => ({ baseTag: getBaseTagFromPropsList( [TAG_PROPERTIES.HREF, TAG_PROPERTIES.TARGET], propsList ), bodyAttributes: getAttributesFromPropsList(ATTRIBUTE_NAMES.BODY, propsList), defer: getInnermostProperty(propsList, HELMET_PROPS.DEFER), encode: getInnermostProperty( propsList, HELMET_PROPS.ENCODE_SPECIAL_CHARACTERS ), htmlAttributes: getAttributesFromPropsList(ATTRIBUTE_NAMES.HTML, propsList), linkTags: getTagsFromPropsList( TAG_NAMES.LINK, [TAG_PROPERTIES.REL, TAG_PROPERTIES.HREF], propsList ), metaTags: getTagsFromPropsList( TAG_NAMES.META, [ TAG_PROPERTIES.NAME, TAG_PROPERTIES.CHARSET, TAG_PROPERTIES.HTTPEQUIV, TAG_PROPERTIES.PROPERTY, TAG_PROPERTIES.ITEM_PROP ], propsList ), // 省略 scriptTags: getTagsFromPropsList( TAG_NAMES.SCRIPT, [TAG_PROPERTIES.SRC, TAG_PROPERTIES.INNER_HTML], propsList ), styleTags: getTagsFromPropsList( TAG_NAMES.STYLE, [TAG_PROPERTIES.CSS_TEXT], propsList ), title: getTitleFromPropsList(propsList), titleAttributes: getAttributesFromPropsList( ATTRIBUTE_NAMES.TITLE, propsList ) }); 見れば分かるように、連想配列を返していますが、その key に linkTags・metaTags・scriptTags・styleTags・title があるのが読めますね。 ここでは、それぞれの連想配列の key で props から 該当するattribute (href や relなど) がある要素(titleやlinkやmetaなど) のみを取り出す関数を呼び出しています。 ここまでで、withSideEffect の最初に段階 reducePropsToState が読めました。 次は、handleClientStateChange (とサーバーサイドなら mapStateOnServer) です。ここでは、state を view に反映させる = head に title や meta や link を書き込むため、canUseDom でクライアントが動かせる場合に動く handleClientStateChange だけを見てみます。 ソースコードの該当箇所は、こちらです。 HelmetUtils.js const handleClientStateChange = newState => { if (_helmetCallback) { cancelAnimationFrame(_helmetCallback); } if (newState.defer) { _helmetCallback = requestAnimationFrame(() => { commitTagChanges(newState, () => { _helmetCallback = null; }); }); } else { commitTagChanges(newState); _helmetCallback = null; } }; newState という引数に、reducePropsToState で返している連想配列が入ります。 newState に defer があろうとなかそうと、commitTagChanges を実行していますね。では、この commitTagChanges を見てみましょう。 HelmetUtils.js const commitTagChanges = (newState, cb) => { const { baseTag, bodyAttributes, htmlAttributes, linkTags, metaTags, noscriptTags, onChangeClientState, scriptTags, styleTags, title, titleAttributes } = newState; updateAttributes(TAG_NAMES.BODY, bodyAttributes); updateAttributes(TAG_NAMES.HTML, htmlAttributes); updateTitle(title, titleAttributes); const tagUpdates = { baseTag: updateTags(TAG_NAMES.BASE, baseTag), linkTags: updateTags(TAG_NAMES.LINK, linkTags), metaTags: updateTags(TAG_NAMES.META, metaTags), noscriptTags: updateTags(TAG_NAMES.NOSCRIPT, noscriptTags), scriptTags: updateTags(TAG_NAMES.SCRIPT, scriptTags), styleTags: updateTags(TAG_NAMES.STYLE, styleTags) }; // 省略 }; 見れば分かるように、body・html タグでは updateAttributes を、titleタグでは updateTitle を、その他のタグでは updateTags を実行しています。 それぞれの実装を見たら、setAttribute や document.title= や appendChild があると思います。 ここまでで、React-Helmet の簡単な流れを把握することができました。 withSideEffect など一見難しそうに見える実装もありましたが、中身を見ると意外と読めますね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

react-router-domのv5, v6の違い

react-router-domのv5, v6の記述方法に若干違いがあるので紹介していきます。 Switch → Routes に変更 v5では、RouteをSwitchで囲みますが、v6では、SwitchをRoutesに変えます。 Route内の記述方法 Route内の記述方法ですが、v5ではexact component={遷移先コンポーネント}のように書いていましたが、v6では exactはいらなくなり、element={<遷移先コンポーネント />} のように書きます。 上記をコードに書くと下記のようになります。 v5の場合 app.js import React from 'react'; import { Container } from '@material-ui/core'; import { BrowserRouter, Switch, Route } from 'react-router-dom'; //Switch import Navbar from './components/Navbar/Navbar'; import Home from './components/Home/Home'; import Auth from './components/Auth/Auth'; const App = () => { return ( <BrowserRouter> <Container maxWidth="lg"> <Navbar /> <Switch> //v5 <Route path="/" exact component={Home} /> //v5 <Route path="/auth" exact component={Home} /> //v5 </Switch> </Container> </BrowserRouter> ); }; export default App; v6の場合 app.js import React from 'react'; import { Container } from '@material-ui/core'; import { BrowserRouter, Routes, Route } from 'react-router-dom'; //Routes import Navbar from './components/Navbar/Navbar'; import Home from './components/Home/Home'; import Auth from './components/Auth/Auth'; const App = () => { return ( <BrowserRouter> <Container maxWidth="lg"> <Navbar /> <Routes> //v6 <Route path="/" element={<Home />} /> //v6 <Route path="/auth" element={<Auth />} /> //v6 </Routes> </Container> </BrowserRouter> ); }; export default App; useHistory → useNavigate に変更 index.js import { useHistory } from 'react-router-dom'; //v5 import { useNavigate } from 'react-router-dom'; //v6 // v5 const history = useHistory(); history.push("/"); history.goBack(); // v6 const navigate = useNavigate(); navigate("/"); navigate(-1); まとめ 個人的にはv6になって直感的に書きやすいなと思っています! 他にも詳しく知りたい方がいれば、下記のリンクを参考にしてみて下さい! 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React RouterでCannot GETと出た時の対応と原因

はじめに URLを直接していると、Cannot GET ...と表示されてしまう。 対処法 webpack.configのdevServerの設定に下記を追加 historyApiFallback: true, 原因 公式ドキュメントを翻訳してみると... HTML5 History API を使用する場合、404 レスポンスの代わりに index.html ページを提供する必要がある可能性があります。devServer.historyApiFallback を true に設定し、有効にします。 SPAみたいに、論理パスを設定することによってルーティングを実装している場合、実際のパスはないため、404エラーを返すけど、上記の設定によって、index.htmlページを返し、そのページに設定してあるルーティング設定をもとにSPAを実現しているようです!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel6.2で「Hello React!」を表示するまで躓いた話

はじめに 個人開発で、Reactを触ってみたいと思い、LaravelにReactをインストールしてコンポーネントを読み込み、ブラウザで Hello React!と表示させるまでに躓いたので忘備録として手順をまとめていきます。 今回、参考にさせて頂いた記事です。 Laravelのインストールは完了していたので、Reactのインストールから説明します。 Reactのインストール ①Laravelのプロジェクトで下記コマンドを実行する。 composer require laravel/ui 1.* 参考記事の通りにすると、composer require laravel/uiだが、バージョン関係のエラーが発生するので上記のようにしました。 ②次に、php artisan ui reactを実行。私の場合はここで下記のようなエラーが発生したので laravelプロジェクト/resources/sass not such file or directory cd resources  //laravelプロジェクト/resources/に移動 mkdir sass      //sassフォルダを作成 上記コマンドを実行して、php artisan ui reactを再度実行すると、下記のようにReactのスカフォールドのインストールが無事成功しました。 React scaffolding installed successfully. Please run "npm install && npm run dev" to compile your fresh scaffolding. Authentication scaffolding generated successfully. Laravelでは、初期のスカフォールドはVueで設定されているのですが、ちゃんとReactに切り替わっているかどうかファイル構成(laravelプロジェクト/resources)でも確認してみます。 実行前 resources ├── assets │ ├── js │ │ ├── app.js │ │ ├── bootstrap.js │ │ ├── components │ │ │ └── Example.vue │ └── sass │ ├── app.scss │ └── _variables.scss 実行後 resources ├── assets │ ├── js │ │ ├── app.js │ │ ├── bootstrap.js │ │ ├── components │ │ │ └── Example.js │ └── sass │ ├── app.scss │ └── _variables.scss 「Example.vue」が「Example.js」に切り替わっています。 これで、php artisan ui reactの役割は果たせたので、インストールメッセージにもある通り、npm install && npm run devを実行してみます。 npm install npm run dev 上記のように実行しようとすると npm run devを実行した時点で下記のようなエラーが発生。 npm ERR! code ELIFECYCLE npm ERR! errno 2 npm ERR! @ development: `cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js` npm ERR! Exit status 2 npm ERR! npm ERR! Failed at the @ development script. npm ERR! This is probably not a problem with npm. There is likely additional logging output above. npm ERR! A complete log of this run can be found in: npm ERR! /Users/user/.npm/_logs/2020-12-23T08_06_14_550Z-debug.log npm ERR! code ELIFECYCLE npm ERR! errno 2 npm ERR! @ dev: `npm run development` npm ERR! Exit status 2 npm ERR! npm ERR! Failed at the @ dev script. npm ERR! This is probably not a problem with npm. There is likely additional logging output above. npm ERR! A complete log of this run can be found in: npm ERR! /Users/user/.npm/_logs/2020-12-23T08_06_14_712Z-debug.log エラーメッセージで検索してみるとピンポイントな解決方法が出てこなかったのですが、npm run devを実行した時のエラー解決方法として下記コマンドを実行している記事が多かったので試してみる。 rm -rf node_modules  (node_moduleを削除) npm cache clear --force (キャッシュを削除) npm install (インストールし直し) npm run dev 再度実行 上記手順でコマンドを実行していくと、さっきまでのエラーは発生しなくなったのですが下記のようなエラーが発生。 Error: mix.react() is now a feature flag. Use mix.js(source, destination).react() instead at React.register (F:\Server\work\reactlaravel\lrpc1\node_modules\laravel-mix\src\components\React.js:15:19) at Object.components.<computed> [as react] (F:\Server\work\reactlaravel\lrpc1\node_modules\laravel-mix\src\components\ComponentRegistrar.js:118:53) at Object.<anonymous> (F:\Server\work\reactlaravel\lrpc1\webpack.mix.js:14:5) at Module._compile (F:\Server\work\reactlaravel\lrpc1\node_modules\v8-compile-cache\v8-compile-cache.js:192:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1157:10) at Module.load (internal/modules/cjs/loader.js:985:32) at Function.Module._load (internal/modules/cjs/loader.js:878:14) at Module.require (internal/modules/cjs/loader.js:1025:19) at require (F:\Server\work\reactlaravel\lrpc1\node_modules\v8-compile-cache\v8-compile-cache.js:159:20) at module.exports (F:\Server\work\reactlaravel\lrpc1\node_modules\laravel-mix\setup\webpack.config.js:3:5) npm ERR! code ELIFECYCLE npm ERR! errno 2 npm ERR! @ development: `mix` npm ERR! Exit status 2 npm ERR! npm ERR! Failed at the @ development script. npm ERR! This is probably not a problem with npm. There is likely additional >logging output above. npm ERR! A complete log of this run can be found in: npm ERR! C:\Users\info\AppData\Roaming\npm-cache\_logs\2021-01-06T04_36_25_320Z-debug.log npm ERR! code ELIFECYCLE npm ERR! errno 2 npm ERR! @ dev: `npm run development` npm ERR! Exit status 2 npm ERR! npm ERR! Failed at the @ dev script. npm ERR! エラーメッセージで検索すると下記の参考記事を発見。 https://stackoverflow.com/questions/65590280/laravel-8-installation-npm-run-dev-error laravelプロジェクト(この部分は皆様のLaravelインストールディレクトリ名に合わせる)/package.json を確認する。 { "private": true, "scripts": { "dev": "npm run development", "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --config=node_modules/laravel-mix/setup/webpack.config.js", "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --config=node_modules/laravel-mix/setup/webpack.config.js", "watch-poll": "npm run watch -- --watch-poll", "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js", "prod": "npm run production", "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --config=node_modules/laravel-mix/setup/webpack.config.js" }, "devDependencies": { "@babel/preset-react": "^7.0.0", "axios": "^0.25.0", "bootstrap": "^4.0.0", "bootstrap-sass": "^3.3.7", "cross-env": "^5.1", "jquery": "^3.2", "laravel-mix": "^6.0.41",//この部分 "lodash": "^4.17.4", "popper.js": "^1.12", "react": "^16.2.0", "react-dom": "^16.2.0", "resolve-url-loader": "^5.0.0", "sass": "^1.49.0", "sass-loader": "^12.4.0" } } 上記のようにlaravel-mixのバージョンが6になっていると、webpack.mix.jsの記述を以下のように変更しないといけないそうなので、変更してみます。 変更前 mix.react('resources/js/app.js', 'public/js') .sass('resources/sass/app.scss', 'public/css'); 変更後 mix.js('resources/js/app.js', 'public/js') .react() .sass('resources/sass/app.scss', 'public/css'); 上記のように変更して、再度npm run devを実行すると下記のように成功メッセージが出力されました! Compiled Successfully in 9076ms webpack compiled successfully 次にwelcome.blade.phpを以下のように書き換え <!doctype html> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- CSRF Token --> <meta name="csrf-token" content="{{ csrf_token() }}"> <title>{{ config('app.name', 'Laravel') }}</title> <!-- Scripts --> <script src="{{ asset('js/app.js') }}" defer></script> <!-- Fonts --> <link rel="dns-prefetch" href="//fonts.gstatic.com"> <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet"> <!-- Styles --> <link href="{{ asset('css/app.css') }}" rel="stylesheet"> </head> <body> <div id="app"></div> //idの値は、後述するApp.js内で利用します。(この部分が、Reactで設定する内容を表示させる為に必要です) </body> </html> 次に laravelプロジェクト/resource/js/app.jsファイルを以下のように書き換える。 require('./bootstrap'); require('./components/App');//このjsファイルは後ほど作成。 最後に、先ほど更新したapp.jsファイルで、読み込むApp.jsファイルを作成します。 import React from 'react'; import ReactDOM from 'react-dom'; function App() { return <h1>Hello React!</h1>; } if (document.getElementById('app')) { ReactDOM.render(<App />, document.getElementById('app')); } 下記部分で、先程定義したwelcome.blade.phpのidがappとなっている箇所に、Hello React!を表示させる関数を呼び出しています。 ReactDOM.render(<App />, document.getElementById('app')); 下記2点のファイル更新を反映させる為に、再度npm run dev実行する。 ・laravelプロジェクト/resource/js/app.js ・laravelプロジェクト/resource/js/components/App.js ブラウザで確認するが、Hello React!が表示されていない、、 この時点で、Laravelのbladeから、Reactのコンポーネントを読み込む方法が合っているのかどうか、色々調べたりしたのですが、知人からアドバイスを頂きスーパーリロードを実行してみると以下のようにHello React!が表示されました!! スクリーンショット 2022-01-27 8.59.51.png ブラウザのキャッシュの更新が出来ていないという初歩的なミスで躓きましたが、無事にHello React!が表示されて良かったです。 おわりに 環境構築は、参考記事を執筆されている方の使用環境と自分の使用環境が違ったりすると、参考記事通りに行かず 躓いてしまう場面が多々あったので、その都度調べて腑に落ちるまで理解しながら進めていくことが大切だと改めて感じました。 Reactを使用した機能で実装したいことがあるので、実装を進めていきながらよりReactの事について深掘りしていけたらと思います。 ここまで閲覧して頂きありがとうございます!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RemixでEmotion(CSS in JS)を使う

この記事の概要 以下の記事に続き、Remixにおけるスタイリングを調査していたため記事にしました。 今回はEmotionを使ってみます。 実験に使っていたリポジトリ EmotionにおけるServer Side Rendering 上記は公式ドキュメントですが、かいつまむと条件にあわせて次のどちらかのアプローチをとることとなります。 :nth-child()などの疑似クラスを使わない場合(Default Approach) renderToString(<App />)をとするだけでOK :nth-child()などの疑似クラスを使う場合(Advanced Approach) 通常の@emotion/reactや@emotion/styledに加えて @emotion/server/create-instanceや@emotion/cacheも用いて AppをCacheProviderでwrapして headにスタイルを差し込む Advanced Approachは箇条書きで書いても伝わりづらいと思うので、是非公式ドキュメントを一読ください。 :nth-child()の類は便利ですが、初めから使わないと決めていればやれないことはありません。 というわけで今回はDefault Approachを採用しました。 導入 前回の記事でのstyled-componentsの実験はかなり苦戦しましたし、最終的にwarningを解消できないまま……。 ところがEmotionは特に苦労することもなく使用できました。 というのも、初めからapp/entry.server.tsxは以下のようになっていてEmotionの言うDefault Approachがクリアされているのです。 app/entry.server.tsx import { renderToString } from "react-dom/server"; import { RemixServer } from "remix"; import type { EntryContext } from "remix"; export default function handleRequest( request: Request, responseStatusCode: number, responseHeaders: Headers, remixContext: EntryContext ) { const markup = renderToString( <RemixServer context={remixContext} url={request.url} /> ); responseHeaders.set("Content-Type", "text/html"); return new Response("<!DOCTYPE html>" + markup, { status: responseStatusCode, headers: responseHeaders }); } ただ、多少今まで通りにいかない箇所もあるためそれらの点について記載しておきます。 注意点 jsx pragmaを省略できない Emotionを単にインストールした状態だと、毎回ファイルの最初に/** @jsx jsx */と記載せねばなりません。 本来は@emotion/babel-preset-css-propや@emotion/babel-pluginの使用によって省略できるのですが、RemixではBabel Pluginが使えないため毎回書く他無さそうです。 Issueこそありますが、対応されないような気もしています……。 また、Babelの設定が変えられない都合上デフォルトではFragmentの省略記法(<></>)も使えません。 以下のどちらかを実施する必要があります。 /* @jsxFrag React.Fragment */と記載する <React.Fragment>(または<Fragment>)と都度記載する double render問題は変わらず またも前回の記事との比較ですが、styled-componentsと比べれば全然許容できる範囲ではあるものの、スタイルが適用されるまでに一瞬レイアウトが崩れています。 styled-compoennts Emotion 実際のプロダクトで使う場合はどうすると良いのでしょうか。 この点は私の中に知見がないため、もし良いアイデアをお持ちの方がいればコメントいただけると非常に嬉しいです!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む