20210405のReactに関する記事は6件です。

React Hook Form 7.0.0アップデートにあたってマイグレーションを行う

7.0.0アップデート React Hook Form 7.0.0へアップデートしたということで、v6.15.1からアップデートしたことで、変更が必要だった点をアウトプット 注意:すべてはmigration guideに書かれている!! 今回の修正コード login.tsx import { useEffect } from 'react' import { useDispatch, useSelector } from 'react-redux' import { jsx } from '@emotion/react'; import { useForm } from "react-hook-form"; import { fetchAsyncLogin, selectUser } from '../../../stores/slices/userSlice' import { BrowserRouter as Router, useHistory } from 'react-router-dom' import { hot } from 'react-hot-loader' import Header from '../../components/block/Header' import Footer from '../../components/block/Footer' import { LoginTitle } from '../../../style/pages/Login' interface loginFormInput { userName: string; passWord: string; } const Login = () => { // 一時的な保持は各コンポーネントで行う const { register, handleSubmit, errors, reset } = useForm<loginFormInput>(); const dispatch = useDispatch() const history = useHistory() // storeに保存されている情報を変数にいれる const user = useSelector(selectUser) const onSubmit = (data: loginFormInput) => { dispatch(fetchAsyncLogin(data)) reset() if (user.isLogin === true) { history.push('/top') } } // Loginを検知してページ遷移 useEffect(() => { console.log(user) if (user.isLogin === true) { history.push('/top') } }, [user.isLogin]) return ( <div> <Header /> <h1 css={LoginTitle}>Login Page</h1> {errors.userName && '入力に不備がございます'} <form onSubmit={handleSubmit(onSubmit)}> <input type="text" name="userName" aria-invalid={errors.userName ? "true" : "false"} ref={register({ required: true })} placeholder="user" /> {errors.userName && errors.userName.type === "required" && <span role="alert">必須だよ!</span>} <input type="text" name="passWord" aria-invalid={errors.passWord ? "true" : "false"} ref={register({ required: true, pattern: /^[A-Za-z]+$/i })} placeholder="pass" /> {errors.passWord && errors.passWord.type === "required" && <span role="alert">必須だよ!</span>} {errors.passWord && errors.passWord.type === "pattern" && <span role="alert">半角英数字でお願いします!</span>} <input type="submit" /> </form> <Footer /> </div> ) } export default hot(module)(Login) プロパティ 'errors' は型 'UseFormReturn' に存在しません。 下記コードで7へアップデートするとタイトルの注意が出てくる。 const { register, handleSubmit, errors, reset } = useForm<loginFormInput>(); migration guideをみてみる。 errors: errors object has been moved into formState object. This will info hook form that errors object is been subscribed. なるほど errosオブジェクトはformStateの中へ移動したと多分書かれています。 - const { register, handleSubmit, errors, reset } = useForm<loginFormInput>(); + const { register, handleSubmit, formState: { errors }, reset } = useForm<loginFormInput>(); これで注意は消えます。 FormRegisterReturn' を型 'LegacyRef | undefined' に割り当てることはできません。プロパティ 'current' は型 'UseFormRegisterReturn' にありませんが、型 'RefObject' では必須です。 以下のコードだとタイトルのような注意が出る。 <input type="text" name="userName" aria-invalid={errors.userName ? "true" : "false"} ref={register({ required: true })} placeholder="user" /> migration guideをみてみる。 register method is no longer occurred at ref, instead invoke the function itself and spread the props into the input. The function itself will return the following props: onChange, onBlur, name and ref. refの中でregisterは呼び出せなくなったぞ!ただregister関数を呼び出し、そこから色々設定できるぞい的なことが書かれているぽい <input type="text" - name="userName" aria-invalid={errors.userName ? "true" : "false"} - ref={register({ required: true })} + {...register("userName", { required: true })} placeholder="user" /> registerの中にname属性が指定できるようになり、name属性の指定がいらなくなりました。 第二引数にrequired trueを付与することで、同じ挙動を再現できます。 今回このような形になったのは以下の理由があるそうです。 register 関数を呼び出すことで型チェックができるようになりました。元のAPIには賛否両論ありますし、型安全性と相性が悪いのは間違いありません。この変更によって、 useController, Controller などのAPIでも型チェックも行ってくれるようになります。(引用: React Hook Formのアップデート内容 - Version 7) 別のinputでバリデーションパターンを設定している箇所があります。こちらも直します。 <input type="text" name="passWord" aria-invalid={errors.passWord ? "true" : "false"} ref={register({ required: true, pattern: /^[A-Za-z]+$/i })} placeholder="pass" /> <input type="text" - name="passWord" aria-invalid={errors.passWord ? "true" : "false"} - ref={register({ required: true, pattern: /^[A-Za-z]+$/i })} + {...register("passWord", { required: true, pattern: /^[A-Za-z]+$/i })} placeholder="pass" /> {}の中で続けて書けば良いって言うだけですね 最後に 以上がアップデートによるマイグレーションでした。 今回は小さめのフォームであったためそこまで大きな変更はなかったのですが、他にも変更箇所はありますので確認した上でアップデートは行っていきましょう。 参考文献 React Hook Formのアップデート内容 - Version 7 Migration Guide
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Redux-sagaで簡単なAPIを動かしてみる

はじめに Reduxを実装したりtoolkitを使っているとAPIと通信しなければならない時があります。 なので簡単なAPIを使ってなるべく簡潔に動作させてみたいと思います。 React Nativeで実装していきます 実現すること 犬と猫の画像をランダムで表示するだけのAPIと通信する 必要なAPIの数が多くなっても管理しやすい形にする 画面実装 この2記事で実装した画面を使いまわします。 上から順にコピペしていくだけでOKです ライブラリをインストールする 公式ドキュメントに書いてあるredux-sagaに加えてaxiosというライブラリもインストールします yarn add redux-saga yarn add axios 入れたライブラリはこんな感じです ライブラリ 用途 redux-saga redux-saga本体 axios サーバーからデータを取得する ライブラリ入れたらpod installもとりあえずやりましょう cd ios && pod install && cd .. react-native run-ios これで下準備が完了!! sliceのファイルにactionを追加する まず最初にuserSliceの中に犬の画像をsetするための記述を追加します payloadのところにはアクションの実行に必要な任意のデータを入れます userSlice.js import {createSlice} from '@reduxjs/toolkit'; const userInitialState = { userName: null, userEmail: null, // stateを追加 dog: null, }; export const userSlice = createSlice({ name: 'user', initialState: userInitialState, reducers: { getInfo: state => { state.userName = 'Tanaka Taro'; state.userEmail = 'practice@example.com'; }, resetInfo: state => { state.userName = userInitialState.userName; state.userEmail = userInitialState.userEmail; }, // payloadしてdogに画像を入れる setImg: (state, {payload}) => { state.dog = payload; }, }, }); export const {getInfo, resetInfo, setImg} = userSlice.actions; ファイルを追加する userディレクトリ配下にAPIと繋ぐためのuserSaga.jsを用意します(userSliceと同じディレクトリに配置) applyAxiosの中には呼び出したいAPI(url)があって、 そこから返ってきた値をcallApiでレスポンスとして受け取っているイメージです。 また、sagaはeffectという「非同期処理を同期的に行うための関数」を使用します。 put , call , takeLatestがその関数にあたります 参考記事 userSaga.js import axios from 'axios'; import {put, call, takeLatest} from 'redux-saga/effects'; import {setImg} from './userSlice'; function applyAxios() { const url = 'https://dog.ceo/api/breeds/image/random'; return axios.get(url); } function* callApi() { try { const res = yield call(applyAxios); yield put(setImg(res)); } catch (error) { console.log(error); } } export const getDog = () => ({type: 'GET_DOG'}); export function* userSaga() { yield takeLatest('GET_DOG', callApi); } todoの方にも反映させる 同様にtodoSliceも修正し、同じディレクトリ内にtodoSagaも作成しましょう todoSlice.js import {createSlice} from '@reduxjs/toolkit'; const todoInitialState = { userTodo: null, cat: null, }; export const todoSlice = createSlice({ name: 'todo', initialState: todoInitialState, reducers: { setTodo: state => { state.userTodo = 'jogging'; }, resetTodo: state => { state.userTodo = todoInitialState.userTodo; }, setImg: (state, {payload}) => { state.cat = payload; }, }, }); export const {setTodo, resetTodo, setImg} = todoSlice.actions; stateを追加 setImgでcatをpayloadする これらの記述を追加しました todoSaga.js import axios from 'axios'; import {put, call, takeLatest} from 'redux-saga/effects'; import {setImg} from './todoSlice'; function applyAxios() { const url = 'https://aws.random.cat/meow'; return axios.get(url); } function* callApi() { try { const res = yield call(applyAxios); yield put(setImg(res)); } catch (error) { console.log(error); } } export const getCat = () => ({type: 'GET_CAT'}); export function* todoSaga() { yield takeLatest('GET_CAT', callApi); } 各sagaをまとめるファイルを作る redux配下にsagas.jsを作成し、その中に作成したsagaをまとめたいと思います (これによりAPIが増えても一元的に管理できる) sagas.jsの中ではyield allを使って各sagaをまとめます sagas.js import {all} from 'redux-saga/effects'; import {userSaga} from './user/userSaga'; import {todoSaga} from './todo/todoSaga'; export default function* rootSaga() { yield all([userSaga(), todoSaga()]); } storeを生成する 個人的にこれはもう暗記しちゃった方が手っ取り早いと思ってます。 さっき作ったsagas.jsの中のrootSagaを、storeに結合して使えるようにする処理です store.js import {configureStore} from '@reduxjs/toolkit'; import {rootReducer} from './reducers'; import createSagaMiddleware from 'redux-saga'; import rootSaga from './sagas'; const sagaMiddleware = createSagaMiddleware(); const store = configureStore({ reducer: rootReducer, middleware: [sagaMiddleware], }); sagaMiddleware.run(rootSaga); export default store; これでRedux-sagaの大まかな実装は終わりです 画面に反映させる 実際に画面上でAPIを受け取って画像を受け取りましょう dispatchしてAPIを叩き、返ってきた画像を受け取るための処理を追加します HomeScreen.js import React from 'react'; import {StyleSheet, View, Text, Button, Image} from 'react-native'; import {getInfo, resetInfo} from '../redux/user/userSlice'; // userSagaから該当するAPIをimport import {getDog} from '../redux/user/userSaga'; import {useSelector, useDispatch} from 'react-redux'; const HomeScreen = ({navigation}) => { const dispatch = useDispatch(); const user = useSelector(state => state.user); // dogというstateがpayloadされた時に入る画像URL const dogImage = user.dog?.data.message; return ( <View style={styles.container}> <Text>私の名前は{user.userName}</Text> <Text>メールアドレスは{user.userEmail}</Text> <Image style={styles.img} source={{ uri: dogImage, }} /> <Button title="セットする" onPress={() => { dispatch(getInfo()); }} /> <Button title="リセットする" onPress={() => { dispatch(resetInfo()); }} /> <Button title="犬を取得" onPress={() => { dispatch(getDog()); }} /> <Button title="画面遷移する" onPress={() => { navigation.navigate('Detail'); }} /> </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', }, input: { width: '50%', borderWidth: 1, }, // 画像表示用のstyle img: { width: '75%', height: '50%', }, }); export default HomeScreen; 犬を取得ボタンを押下した時、犬が表示されたかと思います。 余談ですが小さい頃に実家でコーギーを飼っていて、とても可愛らしい犬でした。。 DetailScreenでも同じことをしましょう getCatをdispatchし、帰ってきた値をImageコンポーネントのuriに入れます DetailScreen.js import React from 'react'; import {StyleSheet, View, Text, Button, Image} from 'react-native'; import {setTodo, resetTodo} from '../redux/todo/todoSlice'; import {getCat} from '../redux/todo/todoSaga'; import {useSelector, useDispatch} from 'react-redux'; const DetailScreen = ({navigation}) => { const dispatch = useDispatch(); const todo = useSelector(state => state.todo); const catImage = todo.cat?.data.file; return ( <View style={styles.container}> <Text>明日のTodoは{todo.userTodo}</Text> <Image style={styles.img} source={{ uri: catImage, }} /> <Button title="セットする" onPress={() => { dispatch(setTodo()); }} /> <Button title="リセットする" onPress={() => { dispatch(resetTodo()); }} /> <Button title="猫を取得" onPress={() => { dispatch(getCat()); }} /> <Button title="Home画面に遷移する" onPress={() => { navigation.navigate('Home'); }} /> </View> ); }; const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', }, img: { width: '75%', height: '50%', }, }); export default DetailScreen; 猫の画像が表示されたかと思います 今は実家に茶色い猫と白い猫の2匹がいます。 駆け足で説明しましたがこれでsagaの大まかな実装が完了しました 最後に 簡単なAPIを繋いで画面に反映させるところまでを実装しました。 変数名(dogやcat)の命名規則に一部統一感のない部分がありますが、sagas.jsでそれぞれのsagaをまとめておくことで通信するAPIが多くなったとしても一元的に管理ができるかと思います。 参考 https://qiita.com/macotok/items/ec5460ac17f5a20c4735 https://dev.classmethod.jp/articles/redux-saga-cheatsheet/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

今まで起きたエラーを管理するアプリ作ってみた

フロントをReact、サーバーサイドをNode、Expressで作成しました。 DBとしてFirebase Cloud Firestoreを使用し、フロントをFirebase Hosting、サーバーをHerokuにデプロイしました。 作ったアプリについて 私は現在エンジニアとしてインターンを2年間やっており、エラーが出たときに「このエラーどっかで見たなぁ」みたいなことが最初の頃よくありました。 今回はこのようなことを解消するためのアプリを作成しました。 使用方法 起きたエラーを入力 カテゴリーを選択(React,Nodeなど) 解決できているならば参考になったURLを記入 メモを書く という流れになります。 今までに起きたエラーを蓄積していくことで、もし同じエラーが起きた場合は、エラーを入力した際に知らせてくれて再び解決策を探す作業が省くことができます。 こちらが作ったアプリ( Error Manager )になりますが 現在はサインアップを実装しておらず、私だけが使えるテスト段階です。 詰まったところ Reactでのログイン認証 色々と調べたところ、しっかりとしたアプリならばセキュリティを考慮して自分でログイン認証を作成するべきではなく、Auth0などのサービスを使用したほうがいいというのが結論でした。 ただ、今回のような小規模な重要な情報を保持しないアプリであれば、LocalStorageやCookieを使って実装してもよいという意見もあり、CookieとFirebase Authenticationで実装しました。 Firebase Authenticationでログイン認証を行い、ログインしたらCookieにID、Passwordを保持し、ログアウトしたらCookieのID、Passwordを削除するという方法になります。 Firebase Authenticationのログイン認証の実装に関しては公式のドキュメントがわかりやすいので詳しい設定はこちらを参考に。 サインイン画面でSubmitボタンを押すと、以下のsigninFunc()が動き、 signInWithEmailAndPasswordでFirebase Authenticationの認証が成功したらCookies.setでemail、password、uid(Firebase Authentication側で勝手に割り振られるID)をCookieに書き込みます。 認証失敗したらEmailもしくはPasswordが間違っている可能性があることをユーザーに知らせます。 Cookieを操作するライブラリはjs-cookieを使用しました。 signin.js const signinFunc = () => { const email = document.getElementById("email").value const password = document.getElementById("password").value firebase.auth().signInWithEmailAndPassword(email, password) .then((user) => { setOpenErr(false) setOpenSuc(true) Cookies.set("email",email,{"expires":7}) Cookies.set("password",password,{"expires":7}) Cookies.set("uid",user.user.uid,{"expires":7}) document.location = "/" }) .catch((error) => { setOpenErr(true) var errorMessage = error.message; console.log(errorMessage) }); } ログイン認証後は、Cookieにあるemail、password、uidを使ってホーム画面に遷移する際に認証します。 App.js import './App.css'; import SignIn from './pages/signIn'; import SignUp from './pages/signUp'; import Main from './pages/main' import { BrowserRouter, Route, Switch } from 'react-router-dom'; import Auth from './components/Auth'; function App() { return ( <BrowserRouter> <Switch> <Route path="/signin" component={SignIn}/> <Auth> <Switch> <Route path="/" exact component={Main} /> </Switch> </Auth> </Switch> </BrowserRouter> ); } export default App; User.js import Cookies from 'js-cookie' import firebase from '../firebase/firebase' export function isLoggedIn(){ const email = Cookies.get("email") const password = Cookies.get("password") if(!(email && password)) return false const isLogin = firebase.auth().signInWithEmailAndPassword(email, password) .then((user) => true) .catch((error) => { const errorMessage = error.message; return false }); return isLogin; } export function logoutFunc(){ Cookies.remove("email") Cookies.remove("password") Cookies.remove("uid") document.location = "/signin" } ログインしていないとホーム画面に遷移できないようにする方法はこちらの記事を参考にしました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[RailsAPI x React]環境構築する方法

作成経緯 RailsAPIモード使い方をまとめたかったので作成してます。備忘録なので所々違うかもしれません。 もっといい方法があればぜひ参考にしたいと考えています。今回はローカルで動くようにしていますので 本番環境では動きません。 RaislAPI作成 $ rails new アプリ名 --api Gemfile gem 'rack-cors' #コメントアウトを外してbundle installする config/initializers/cors.rb #コメントアウトを外す Rails.application.config.middleware.insert_before 0, Rack::Cors do allow do origins 'http://localhost:3001' #変更します。 #origins 'example.com' #'http://localhost:3001'許可する resource '*', headers: :any, methods: [:get, :post, :put, :patch, :delete, :options, :head] end end モデル作成 $ rails g migration モデル名 $ bundle exec rails db:migrate 普段と変わらないのでいつも通り作成してください。 ルーティング作成 config/routes.rb Rails.application.routes.draw do #----------ここから---------------- namespace :api do namespace :v1 do resources :todos, only: %i[index show create update destroy] end end #----------ここまで追加------------- end バージョン管理する時にnamespaceを使うと便利なので分けています。 コントローラ作成 app/controllers/api/v1/todos_controller.rb class Api::V1::TodosController < ApplicationController def index todos = Todo.order(updated_at: :desc) render json: todos end def show todo = Todo.find(params[:id]) render json: todo end def create todo = Todo.new(todo_params) if todo.save render json: todo else render json: todo.errors, status: 422 end end def update todo = Todo.find(params[:id]) if todo.update(todo_params) render json: todo else render json: todo.errors, status: 422 end end def destroy if Todo.destroy(params[:id]) head :no_content else render json: { error: "Failed to destroy" }, status: 422 end end private def todo_params params.require(:todo).permit(:name, :is_completed) end end 今回はJSON形式でデータを返すだけです。 destroyは返すデータがないためheadを使っています。 headについてはRailsガイド確認 controllerの書き方Part2 app/controllers/api/v1/todos_controller.rb module Api module V1 class TodosController < ApplicationController def index todos = Todo.all render json: { todos: todos }, status: :ok end end end end module ディレクトリ〜end囲んでもnamespace使うことが出来ます。 React作成 アプリの直下でReactのアプリ作成する。 $ npx create-react-app front $ cd front $ npm install react-router-dom $ npm install axios $ npm start Reactの初期画面が出ていることを確認します。 $ mkdir src/urls $ touch src/urls/index.js src/urls/index.js const DEFAULT_API_LOCALHOST = 'http://localhost:3000/api/v1' export const todosIndex = `${DEFAULT_API_LOCALHOST}/todos` axiosで通信するものを定数で定義します。 src/apis/todos.js import axios from 'axios'; import { todosIndex } from '../urls/index' export const fetchTodos =() => { return axios.get(todosIndex) .then(res => { return res.data //通信成功 }) .catch((e) => console.error(e)) //通信失敗 } axios.getでHTTPリクエストをしています。 通信に成功した場合は.then()なり、通信に失敗・例外の場合は.catch()になります。 作成した関数fetchTodos()呼び出したら'http://localhost:3000/api/v1/todosを呼び出し、JSON形式でデータが返って来ます。 .then(res => { return res.data //通信成功 }) resの中にデータが入り、returnでres.dataデータだけ返しています。 import React, { Fragment, useEffect } from 'react'; import { fetchTodos } from '../apis/todos'; export const Todos = () => { useEffect(() => { fetchTodos() .then((data) => console.log(data) ) }, []) return ( <Fragment> Todo一覧 </Fragment> ) } rails側でrails sをし、localhost:3000立ち上げ、 front側でnpm startします。localhost:3000はrailsで使用しているためlocalhost:3001で立ち上げます。 これで環境構築は完成していると思います。 毎回、railsとreactを両方立ち上げるのがめんどくさいので 今後の目標はDockerで環境構築したいと考えています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

data-driven-formsでバリデーションを実装

data-driven-formsでバリデーションを実装 やりたいこと テキストボックスに桁数、最大、最小、全角、半角のバリデーションを入れる。 実装 codeSandboxでバリデーションを入れたテキストボックスを作成しました。 コード全文は下記の通りです。 App.js import React from "react"; import FormRenderer from "@data-driven-forms/react-form-renderer/form-renderer"; import componentTypes from "@data-driven-forms/react-form-renderer/component-types"; import validatorTypes from "@data-driven-forms/react-form-renderer/validator-types"; import TextField from "@data-driven-forms/mui-component-mapper/text-field"; import FormTemplate from "@data-driven-forms/mui-component-mapper/form-template"; const schema = { title: "Validator mapper", fields: [ { component: componentTypes.TEXT_FIELD, name: "桁数チェック", label: "桁数チェック", helperText: "5文字以上10文字以下", validate: [ { type: validatorTypes.MIN_LENGTH, threshold: 5, message: "5文字以上で入力してください。" }, { type: validatorTypes.MAX_LENGTH, threshold: 10, message: "10文字以下で入力してください。" } ] }, { component: componentTypes.TEXT_FIELD, name: "数値の最大最小", label: "数値の最大最小", helperText: "1以上30以下", type: "number", validate: [ { type: validatorTypes.MIN_NUMBER_VALUE, includeThreshold: true, value: 1, message: "1以上30以下で入力してください。" }, { type: validatorTypes.MAX_NUMBER_VALUE, includeThreshold: true, value: 30, message: "1以上30以下で入力してください。" } ] }, { label: "半角チェック", component: componentTypes.TEXT_FIELD, name: "半角チェック", helperText: "半角英数のみ", validate: [{ type: "halfwidth" }] }, { label: "全角チェック", component: componentTypes.TEXT_FIELD, name: "全角チェック", helperText: "全角英数かな", validate: [{ type: "fullwidth" }] } ] }; const componentMapper = { [componentTypes.TEXT_FIELD]: TextField }; const validatorMapper = { halfwidth: ({ message }) => (value) => { if (!value) { return; } if (!value.match(/^[a-zA-Z0-9]+$/)) { return message || "半角入力のみ"; } }, fullwidth: ({ message }) => (value) => { if (!value) { return; } if (!value.match(/^[a-zA-Z0-9ぁ-んァ-ン]+$/)) { return message || "全角入力のみ"; } } }; const ValidatorMapper = () => ( <FormRenderer FormTemplate={FormTemplate} validatorMapper={validatorMapper} componentMapper={componentMapper} schema={schema} onSubmit={console.log} /> ); export default ValidatorMapper; 1. 桁数のバリデーション Length-Validatorを使う MIN_LENGTHでテキストの最小の長さを、MAX_LENGTHでテキストの最大の長さをチェックします。また、EXACT_LENGTHでテキストの正確な長さをチェックします。 バリデーターの閾値はthreshold: numberで指定します。 2. 数字の最大値、最小値のバリデーション Number value Validatorを使う MIN_NUMBER_VALUEで数値が値よりも小さいかどうか、MAX_NUMBER_VALUEで数値が値よりも大きいかどうかをチェックします。 includeThreshold: true,の場合は閾値を含む範囲、includeThreshold: false,の場合は閾値を含まない範囲となり、デフォルトはtrueとなっています。 3. 全角、半角のバリデーション Custom Validatorを使う data-driven-formsのデフォルトでは全角、半角のバリデーションがなかったため、カスタムバリデーターを使って自作します。 正規表現を使って半角のみ、全角のみのvalidatorMapperを作成。 半角入力のみをhalfwidth、全角入力のみをfullwidthとしています。 App.js const validatorMapper = { halfwidth: ({ message }) => (value) => { if (!value) { return; } if (!value.match(/^[a-zA-Z0-9]+$/)) { return message || "半角入力のみ"; } }, fullwidth: ({ message }) => (value) => { if (!value) { return; } if (!value.match(/^[a-zA-Z0-9ぁ-んァ-ン]+$/)) { return message || "全角入力のみ"; } } }; schemaのバリデートのタイプにvalidatorMapperの中で定義したものを指定する。 半角入力のみのバリデーションを行う場合  validate: [{ type: "halfwidth" }] 半角入力のみのバリデーションを行う場合  validate: [{ type: "fullwidth" }] 最後に エラー時のエラーメッセージはvalidateの中でmessage: "エラーメッセージの内容"とすることで表示させることができます。 また、半角、全角チェックの他に、数字のみやemail、郵便番号といった入力チェックを行いたい場合もカスタムバリデーター、正規表現を使って対応可能だと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React-routerで直前のパスに戻す

結論 this.props.history.goBack() これでOK どういう事ができるの? 例えば複数のページに固定されたリンク(ヘッダーからモーダルを開くなど)を実装した際にモーダルを閉じたときに前のページに遷移させて ユーザーの手間を省くことができる。 こういう小さな知識でユーザーの使い勝手が変わるので日々勉強するの大事ですね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む