20220117のReactに関する記事は7件です。

【React】外部リンクに飛ぶ方法【Javascript】

外部のページに飛びたい ReactでSPAアプリ作成時にReactRouter外の外部のページに飛びたい。 しかし、useHistoryやLinkでは飛べない。 そういう場面に直面して解決したのでその方法をここに記します。 実装方法 リンクで飛ぶ //現在のタブで開く <a href="https://www.google.com/">Text</a> //新しいタブで開く <a target="_blank" href="https://www.google.com/">Text</a> 何らか処理後に飛ぶ export const Hoge () => { 現在のタブで開く function onClickHandleA () { window.location.replace("https://www.google.com/") } //新しいタブで開く function onClickHandleB () { window.open("https://www.google.com/", '_blank'); } return ( <div> <div onclick={onClickHandleA }> Text </div> <div onclick={onClickHandleB }> Text </div> </div> ) } 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

備忘録 React Reduxエラー "export 'Switch' was not found in 'react-router-dom'で

少し古いガイドラインに沿ってアプリ制作をしていたら、いきなりSwitchがないというエラー。 react-router-domのバージョンが原因。v6以降はSwitchがRoutesになり書き方に変更点が数カ所。 ①v5に戻す、②v6の書き方へ変更するの2種類の対処法をまとめました。 対処法① バージョンをv5に戻す 以下のコマンドで、現行バージョンをアンインストールして、v5をインストールし直す。 npm uninstall react-router-dom npm install react-router-dom@5.2.0 対処法② Switch → Routesに変更 index.jsx<変更前> import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import { createStore } from 'redux'; import { BrowserRouter, Route, Switch } from 'react-router-dom'; import App from './components/app'; const store = createStore() ReactDOM.render( <Provider store={store}> <BrowserRouter> <Switch> <Route path="/channels/:channel" component={App} /> </Switch> </BrowserRouter> </Provider>, document.getElementById("root"); ); index.jsx<変更前> import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import { createStore } from 'redux'; import { BrowserRouter, Route, Routes } from 'react-router-dom'; import App from './components/app'; const store = createStore() ReactDOM.render( <Provider store={store}> <BrowserRouter> <Routes> <Route path="/" element={<App />} /> </Routes> </BrowserRouter> </Provider>, document.getElementById("root"); ); importの変更 Switch→Routesに変更 component→element {App}→{}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ReactのRefの使い方(初心者向け)

最近React Refを勉強したのですが、大体の初心者向けに書かれている記事に記載されている使い方がRefの使用用途としてはあまりしっくり来なかったので、自分用メモとして残しておきます。 React Refの使い道 RefはDOMを参照するために使うことが出来て、テキストフォーカスや、テキスト選択、アニメーションやサードパーティーDOMライブラリに使うことが出来ます。 ここではDOMに変更を入れる例を書いてみました。(テキストフォーカスする例は死ぬほどあるので...) App.js import { useRef } from "react"; export default function App() { const ref = useRef(null); return ( <> <button onClick={() => { const li = document.createElement("li"); li.innerText = "test"; ref.current.appendChild(li); }} > Click </button> <ul ref={ref}></ul> </> ); } ちなみに自作コンポーネントへrefを渡す際は、forwardRefを使わないとrefが使えないので、そちらを使った例についても書いてみました。 App.js import { forwardRef, useRef } from "react"; const Input = forwardRef((props, ref) => { return <input type="text" ref={ref} {...props} />; }); export default function App() { const ref = useRef(null); return ( <> <Input ref={ref} /> <button onClick={() => { ref.current.value = "test"; }} > Click </button> </> ); }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

"use-shopping-cart"を使って、StripeとReactで簡単にECカート機能 + 決済機能を実装しよう

Stripeを利用することで、決済機能を簡単に実装することができます。 しかし通販機能を実装するためには、決済だけではなく買い物かご(カート)機能も実装する必要があります。 今回は、「use-shopping-cart」を利用して、Reactアプリで簡単にカート機能を実装する方法を紹介します。 use-shopping-cartとは use-shopping-cartは、Reactでショッピングカート機能を実装するために必要な機能を備えたHookを提供するライブラリです。 内部的にReduxを利用しており、「カート機能」と「Stripe Checkoutへの遷移」の2機能が利用できます。 use-shopping-cartでできること(一部) use-shopping-cartを利用することで、一般的なショッピングカートを実装するために必要なAPIを簡単に手に入れることができます。 商品をカートに追加する カートに追加した商品データや合計金額データを取得する 商品をカートから削除する カート内の商品の個数を変更する etc... use-shopping-cartを使ってカート機能を実装しよう サンプルアプリのセットアップ Create React AppやViteを利用してアプリをセットアップしましょう。 // CRA % npx create-react-app my-app // Vite % npm init vite ライブラリをインストールしよう useShoppingCartライブラリをインストールします。 この際Stripe.jsも一緒にインストールします。 ただしReact SDKは不要ですのでご注意ください。 % npm install @stripe/stripe-js use-shopping-cart CartProviderを設定しよう use-shopping-cartは内部的にReduxを利用しています。 そのため、アプリ全体をCardProviderの子要素にする必要があります。 import ReactDOM from 'react-dom' import { CartProvider } from 'use-shopping-cart' import App from './App' ReactDOM.render( <CartProvider mode="payment" cartMode="client-only" stripe={YOUR_STRIPE_API_KEY_GOES_HERE} successUrl="http://localhost:3000" cancelUrl="http://localhost:3000" currency="JPY" allowedCountries={['US', 'GB', 'JP']} billingAddressCollection={true} > <App /> </CartProvider>, document.getElementById('root') ) loadStripeやredirectToCheckoutなどで設定するパラメータをここで設定します。 Providerで設定することで、redirectToCheckoutを呼び出す際にリダイレクトURLなどの情報を省略することができます。 cartModeでStripe Checkoutの起動方法が変更可能です cartModeには、checkout-sessionとclient-onlyの2種類が設定できます。 client-onlyを利用した場合は、checkout.sessions.createAPIを利用せずに、クライアント側だけでStripe Checkoutへのリダイレクトを実施します。 APIでCheckoutのセッションを作成したい場合は、checkout-sessionを利用しましょう。 商品・料金データを設定しよう 商品データを作成します。簡単な方法はJSONまたはオブジェクトで設定することです。 price_idとpriceの2つが欠けていると、うまく動作しませんので注意しましょう。 const productData = [ { name: 'Bananas', price_id: 'price_xxxx', price: 400 }, { name: 'Tangerines', price_id: 'price_xxxx', price: 100 } ] 続いて設定した商品データをReactで表示させましょう。 import { formatCurrencyString } from 'use-shopping-cart' ... <ul> {productData.map(product => ( <li key={product.price_id}> {product.name} ({formatCurrencyString({ value: product.price, currency: 'JPY' })}) <br /> <button> Add 1 to Cart </button> </li> ))} </ul> formatCurrencyStringを利用することで、金額を指定した通貨に合わせた表示に変換することができます。 カート機能を実装する いよいよカート機能の実装に入ります。 カートに関するメソッドなどは、useShoppingCartフックを利用して取得します。 import { useShoppingCart } from 'use-shopping-cart' ... const { totalPrice, cartCount, addItem, cartDetails, ...args } = useShoppingCart() カートに商品を追加する機能を実装しよう まずは表示している商品をカートに追加するボタンを実装しましょう。 import { formatCurrencyString, useShoppingCart } from 'use-shopping-cart' const Shop = () => { const { addItem } = useShoppingCart() return ( <div> <ul> {productData.map(product => ( <li key={product.price_id}> {product.name} ({formatCurrencyString({ value: product.price, currency: 'JPY' })}) <br /> <button onClick={() => addItem(product)}> Add 1 to Cart </button> </li> ))} </ul> </div> ) } buttonのonClickイベントにaddItemを設定しました。 これで選択した商品をカートに追加完了です。 カートの中身を確認しよう 追加に成功しましたが、実際にカートの中に何が登録されているかがわかりません。 そこでカートの中身についても表示させてみましょう。 const Shop = () => { const { addItem, + cartDetails, } = useShoppingCart() return ( <div> ... <section> <h1>Cart</h1> <ul> {Object.entries(cartDetails).map(([priceId, cartItem]) => ( <li key={`cart-${priceId}`}> {cartItem.name} ({cartItem.formattedPrice} * {cartItem.quantity} = {cartItem.formattedValue})<br/> <button>Increase</button> <button>Decrease</button> <button>Cancel</button> </li> ))} </ul> </section> </div> ) } cartDetailsを使用することで、カートに追加された商品データを取得・表示することができます。 # カートの商品数を変更できるようにしよう カートに追加した商品の数を増減させたり、削除することもできます。 以下のサンプルを参考に、コードを更新しましょう。 const Shop = () => { const { addItem,  cartDetails, +     incrementItem, decrementItem, removeItem, } = useShoppingCart() return ( <div> ... <section> <h1>Cart</h1> <ul> {Object.entries(cartDetails).map(([priceId, cartItem]) => ( <li key={`cart-${priceId}`}> {cartItem.name} ({cartItem.formattedPrice} * {cartItem.quantity} = {cartItem.formattedValue})<br/> + <button onClick={() => incrementItem(priceId)}>1つ増やす</button> + <button onClick={() => decrementItem(priceId)}>1つ減らす</button> + <button onClick={() => removeItem(priceId)}>削除</button> </li> ))} </ul> </section> </div> ) } これだけでカートの中身を増減や削除できるようになりました。 Stripe Checkoutにリダイレクトさせて、注文できるようにしよう 最後にカートに登録した商品を注文できるようにしましょう。 これもuseShoppingCartで取得できるメソッドを利用するだけです。 const Shop = () => { const { addItem,  cartDetails,     incrementItem, decrementItem, removeItem, + redirectToCheckout, } = useShoppingCart() return ( <div> ... + <button onClick={() => redirectToCheckout()}> + 注文する + </button> </div> ) } 追加した「注文する」ボタンをクリックすると、カートに追加した商品・料金データと個数が反映されたCheckoutページが表示されます。 Appendix: カート情報はどこに保存されている? 実装を見る限り、カートの情報はlocalStorageに保存されています。 そのため、途中でユーザーがサイトから離脱した場合でも、同じブラウザであればカートの内容を維持することができます。 終わりに use-shopping-cartの基本的な機能について紹介しました。 APIでCheckoutセッションを作成した場合など、ここでは紹介できていないユースケースもまだまだありますので、また改めて紹介したいと思います。 [PR] Stripe開発者向け情報をQiitaにて配信中! 2021年12月よりQiitaにて、Stripe開発者のためのブログ記事更新を開始しました。 [Stripe Updates]:開発者向けStripeアップデート紹介・解説 ユースケース別のStripe製品や実装サンプルの紹介 Stripeと外部サービス・OSSとの連携方法やTipsの紹介 初心者向けのチュートリアル(予定) など、Stripeを利用してオンラインビジネスを始める方法について随時更新してまいります。 -> Stripe Organizationsをフォローして最新情報をQiitaで受け取る
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React】import * as React と import React の違い

はじめに  本記事は、プログラミング初学者が、学習を進めていて疑問に思った点について調べた結果を備忘録も兼ねてまとめたものです。  そのため、記事の内容に誤りが含まれている可能性があります。ご容赦ください。  間違いを見つけた方は、お手数ですが、ご指摘いただけますと幸いです。 import * as React と import React の違い Reactでコンポーネントをインポートする際に以下の2通りの記述をよく見かけます。 import * as React from 'react'; import React from 'react'; import * as React from 'react'; import * as React from 'react';はreactでexportされているもの全てをimportしてReactという変数に格納する記述です。 以下のようになります。 Sample.js // named export export const test = "Hoge"; // default import const Default = "Fuga"; export default Default; App.js import * as Sample from './Sample'; console.log(Sample); /* { test: "Hoge", default: "Default" } */ import React from 'react'; import * as React from 'react';はreactでexportされているもの全てをimportしてReactという変数に格納する記述です。 以下のようになります。 Sample.js // named export export const test = "Hoge"; // default import const Default = "Fuga"; export default Default; App.js // default import import Sample from './Sample'; console.log(Sample); /* { test: "Hoge", default: "Default" } */ まとめ import * as ~は、バンドラーを使用した際にビルドファイルが肥大化する恐れがあります。 ビルドファイルの肥大化をため、なるべくdefault importもしくは、named importを使用しましょう。 なお、default importは、各自が名前を決められてしまうため、named exportとnamed importを使用するのが良いようです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Remixでのスタイリングに関する調査(2022年1月現在)

この記事の概要 巷で話題のRemixですが、スタイリングに関する記事はあまり見当たりません。 そのため自分で調査してまとめてみました。 まだ変化も大きいと思いますが、直近で導入を考えている人にとって役立てば幸いです。 公式ドキュメントにある情報 主な方法 Remixは<link rel="stylesheet">の形式でスタイルを読み込み、適用するのが基本方針のようです。 以下の2つの方法が示されていますが、自前のスタイルを一切書かなくて済むサイトは少ないでしょう。 というわけで、実質的に2番目の方法は必須だと思われます。 リモートのスタイルシートを用いる 通常のスタイルシートを用いる リモートのスタイルシートを用いる 公式ドキュメントから例を拝借します。 app/root.tsx import type { LinksFunction } from "remix"; export const links: LinksFunction = () => { return [ { rel: "stylesheet", href: "https://unpkg.com/modern-css-reset@1.4.0/dist/reset.min.css" } ]; }; 例が示しているように、実質reset系のCSSやCSSフレームワークなど、サイト全域で使うものを指定することになるんだと思います。 もちろん`root.tsx`以外でもこの書き方自体はできます。 ですが、importするだけして未使用だった場合に警告は出ないなど管理が煩雑になる印象しかありませんでした。 通常のスタイルシートを用いる CSSファイルを作成する 各コンポーネントのファイルでstyleをimport 各コンポーネントのファイルでfunction links()としてexport 各ページのファイルでlinks as XXLinksといった形式でimport 各ページのファイルのlinks()内で、スプレッド構文でXXLinksを展開 結構大変なのですが、イメージがつきづらい気がするので自分で試していたときのコードを載せます。 いくつかのコンポーネントを作り、それらをimportしてページを作る、という至って普通のシーンなのですが……。 コンポーネントのファイル(他にもいくつか存在する) import { ComponentPropsWithRef, forwardRef } from "react"; import styles from "~/styles/footer.css"; type Props = { className?: string; } & ComponentPropsWithRef<"footer">; export const links = () => [{ rel: "stylesheet", href: styles }]; export const Footer = forwardRef<HTMLElement, Props>( ({ className, ...props }, ref) => { return ( // 省略 ); } ); stylesファイルに作成したCSSをimportしつつ、このコンポーネントからもexportしています。 ページのファイル import { Header, links as headerLinks } from "~/components/RegularStylesheets/Header"; import { Navigation, links as navigationLinks } from "~/components/RegularStylesheets/Navigation"; import { FeedItem, links as feedItemLinks } from "~/components/RegularStylesheets/FeedItem"; import { Ranking, links as rankingLinks } from "~/components/RegularStylesheets/Ranking"; import { Footer, links as footerLinks} from "~/components/RegularStylesheets/Footer"; import globalStyles from "~/styles/global.css"; import pageStyles from "~/styles/page.css" export function links() { return [ { rel: "stylesheet", href: globalStyles }, ...headerLinks(), ...navigationLinks(), ...feedItemLinks(), ...rankingLinks(), ...footerLinks(), { rel: "stylesheet", href: pageStyles }, ]; } export default function SomePage() { return ( // 省略 ); } それぞれのコンポーネントで定義したlinksをasを使って別名でimportし、links()の中で展開しています。 コンポーネントが増える度にこちらの指定も増えるので、大した手間では無いものの面倒くさいのは否めません。 あと、これはあくまでimportやexportの仕方であってクラス名の衝突を防ぐのは自分で頑張る必要があります。 この方法のときはRemixが面倒を見てはくれません。 Tailwind CSS 現状ではTailwindで書くのが唯一の方法な印象です。 1番導入が楽で、挙動の怪しい箇所も無さそうでした。 JITモードやapplyを使うかどうかはチームのルール次第なのでここでは触れません。 styled-components 動くと言えば動きますが、全体的に怪しいです。 まず、公式にあるexampleの通りだと読み込み時に一瞬ちらつきが発生します。 styleに埋め込まれるはずの文字列が一瞬画面全体に描画されてしまいました。 リロードを繰り返している様子 色々調べた末、こちらのGistを参考にして書いたら一応チラつきは改善されました。 ですが……PropclassNamedid not match. Server: "foo" Client: "bar"をwarningが出るようになってしまい最終的に解決まで至れませんでした……。 一応、今私が書いたコードを置いておきます。 究明まで至れずすみません。 entry.server.tsx import { renderToString } from "react-dom/server"; import { RemixServer } from "remix"; import type { EntryContext } from "remix"; import { ServerStyleSheet } from "styled-components"; import StylesContext from "./StylesContext"; export default function handleRequest( request: Request, responseStatusCode: number, responseHeaders: Headers, remixContext: EntryContext ) { const sheet = new ServerStyleSheet(); try { let html = renderToString( sheet.collectStyles( <StylesContext.Provider value={null}> <RemixServer context={remixContext} url={request.url} /> </StylesContext.Provider> ) ); const styles = sheet.getStyleTags(); html = html.replace("</head>", `${styles}</head>`); responseHeaders.set("Content-Type", "text/html"); return new Response("<!DOCTYPE html>" + html, { status: responseStatusCode, headers: responseHeaders, }); } catch (error) { console.log(error); } finally { sheet.seal(); } } PostCSS あくまでautoprefixerなどの付与がメインだと思うので今回は検証していません。 挙がっているIssueなど 調査をする中で見つけたIssueを貼っておきます。 styled-componentsでのチラつきの話はRemix側にもstyled-components側にもIssueが立っており、まだ解決できなさそうな雰囲気を感じています。 Remix styled-components vanilla-extract stitches
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RemixでのCSSまわりに関する調査(2022年1月現在)

この記事の概要 巷で話題のRemixですが、スタイリングに関する記事はあまり見当たりません。 そのため自分で調査してまとめてみました。 まだ変化も大きいと思いますが、直近で導入を考えている人にとって役立てば幸いです。 公式ドキュメントにある情報 主な方法 Remixは<link rel="stylesheet">の形式でスタイルを読み込み、適用するのが基本方針のようです。 以下の2つの方法が示されていますが、自前のスタイルを一切書かなくて済むサイトは少ないでしょう。 というわけで、実質的に2番目の方法は必須だと思われます。 リモートのスタイルシートを用いる 通常のスタイルシートを用いる リモートのスタイルシートを用いる 公式ドキュメントから例を拝借します。 app/root.tsx import type { LinksFunction } from "remix"; export const links: LinksFunction = () => { return [ { rel: "stylesheet", href: "https://unpkg.com/modern-css-reset@1.4.0/dist/reset.min.css" } ]; }; 例が示しているように、実質reset系のCSSやCSSフレームワークなど、サイト全域で使うものを指定することになるんだと思います。 もちろん`root.tsx`以外でもこの書き方自体はできます。 ですが、importするだけして未使用だった場合に警告は出ないなど管理が煩雑になる印象しかありませんでした。 通常のスタイルシートを用いる CSSファイルを作成する 各コンポーネントのファイルでstyleをimport 各コンポーネントのファイルでfunction links()としてexport 各ページのファイルでlinks as XXLinksといった形式でimport 各ページのファイルのlinks()内で、スプレッド構文でXXLinksを展開 結構大変なのですが、イメージがつきづらい気がするので自分で試していたときのコードを載せます。 いくつかのコンポーネントを作り、それらをimportしてページを作る、という至って普通のシーンなのですが……。 コンポーネントのファイル(他にもいくつか存在する) import { ComponentPropsWithRef, forwardRef } from "react"; import styles from "~/styles/footer.css"; type Props = { className?: string; } & ComponentPropsWithRef<"footer">; export const links = () => [{ rel: "stylesheet", href: styles }]; export const Footer = forwardRef<HTMLElement, Props>( ({ className, ...props }, ref) => { return ( // 省略 ); } ); stylesファイルに作成したCSSをimportしつつ、このコンポーネントからもexportしています。 ページのファイル import { Header, links as headerLinks } from "~/components/RegularStylesheets/Header"; import { Navigation, links as navigationLinks } from "~/components/RegularStylesheets/Navigation"; import { FeedItem, links as feedItemLinks } from "~/components/RegularStylesheets/FeedItem"; import { Ranking, links as rankingLinks } from "~/components/RegularStylesheets/Ranking"; import { Footer, links as footerLinks} from "~/components/RegularStylesheets/Footer"; import globalStyles from "~/styles/global.css"; import pageStyles from "~/styles/page.css" export function links() { return [ { rel: "stylesheet", href: globalStyles }, ...headerLinks(), ...navigationLinks(), ...feedItemLinks(), ...rankingLinks(), ...footerLinks(), { rel: "stylesheet", href: pageStyles }, ]; } export default function SomePage() { return ( // 省略 ); } それぞれのコンポーネントで定義したlinksをasを使って別名でimportし、links()の中で展開しています。 コンポーネントが増える度にこちらの指定も増えるので、大した手間では無いものの面倒くさいのは否めません。 あと、これはあくまでimportやexportの仕方であってクラス名の衝突を防ぐのは自分で頑張る必要があります。 この方法のときはRemixが面倒を見てはくれません。 Tailwind CSS 現状ではTailwindで書くのが唯一の方法な印象です。 1番導入が楽で、挙動の怪しい箇所も無さそうでした。 JITモードやapplyを使うかどうかはチームのルール次第なのでここでは触れません。 styled-components 動くと言えば動きますが、全体的に怪しいです。 まず、公式にあるexampleの通りだと読み込み時に一瞬ちらつきが発生します。 styleに埋め込まれるはずの文字列が一瞬画面全体に描画されてしまいました。 リロードを繰り返している様子 色々調べた末、こちらのGistを参考にして書いたら一応チラつきは改善されました。 ですが……PropclassNamedid not match. Server: "foo" Client: "bar"をwarningが出るようになってしまい最終的に解決まで至れませんでした……。 一応、今私が書いたコードを置いておきます。 究明まで至れずすみません。 entry.server.tsx import { renderToString } from "react-dom/server"; import { RemixServer } from "remix"; import type { EntryContext } from "remix"; import { ServerStyleSheet } from "styled-components"; import StylesContext from "./StylesContext"; export default function handleRequest( request: Request, responseStatusCode: number, responseHeaders: Headers, remixContext: EntryContext ) { const sheet = new ServerStyleSheet(); try { let html = renderToString( sheet.collectStyles( <StylesContext.Provider value={null}> <RemixServer context={remixContext} url={request.url} /> </StylesContext.Provider> ) ); const styles = sheet.getStyleTags(); html = html.replace("</head>", `${styles}</head>`); responseHeaders.set("Content-Type", "text/html"); return new Response("<!DOCTYPE html>" + html, { status: responseStatusCode, headers: responseHeaders, }); } catch (error) { console.log(error); } finally { sheet.seal(); } } PostCSS あくまでautoprefixerなどの付与がメインだと思うので今回は検証していません。 挙がっているIssueなど 調査をする中で見つけたIssueを貼っておきます。 styled-componentsでのチラつきの話はRemix側にもstyled-components側にもIssueが立っており、まだ解決できなさそうな雰囲気を感じています。 Remix styled-components vanilla-extract stitches
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む