質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Redux

Reduxは、JavaScriptアプリケーションの状態を管理するためのオープンソースライブラリです。ReactやAngularで一般的にユーザーインターフェイスの構築に利用されます。

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

Q&A

解決済

2回答

3449閲覧

Redux Toolkit で useDispatch が実行できません | React Hooks + Redux Toolkit + TypeScript

haku-bot

総合スコア1

Redux

Reduxは、JavaScriptアプリケーションの状態を管理するためのオープンソースライブラリです。ReactやAngularで一般的にユーザーインターフェイスの構築に利用されます。

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

0グッド

0クリップ

投稿2020/05/27 16:46

編集2020/05/27 16:55

前提・実現したいこと

React + TypeScriptにてSPAを作っており、React HooksとRedux Toolkitで状態管理を行っています。

発生している問題・エラーメッセージ

useStateにて状態の初期値は取得出来るのですが、useDispatchによるactionが実行されません。
ログイン状態を管理しようと試みていたのでstoreのファイル名がauth.tsになっておりますが、中身はデバッグのためのコードです。

useDipatchのアクションが動いていればコンソール表示は

Initial state: hoge Action: fuga Update state: fuga

と期待出来るはずですが、以下の状態でございます。

イメージ説明

該当のソースコード

# pages/SignUp/index.tsx import React from 'react'; import { useDispatch, useSelector } from "react-redux"; import { googleSignup } from "../../stores/auth"; function SignUp() { const dispatch = useDispatch(); const hoge = useSelector<any>(state => state.auth.name); function handleSubmit() { console.log('Initial state: ' + hoge) dispatch(googleSignup) console.log('Update state: ' + hoge) } return ( <div> <h1>SignUp</h1> <button onClick={handleSubmit}>Google Sign-up</button> </div> ) } export default SignUp
# stores/auth.ts import { createSlice } from '@reduxjs/toolkit'; export type State = { name: string } const initialState: State = { name: 'hoge' }; const slice = createSlice({ name: 'auth', initialState, reducers: { googleSignup(state: State) { state.name = 'fuga' console.log('Action:' + state.name) } } }); export const { googleSignup } = slice.actions; export default slice.reducer;
# /index.ts import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from "react-redux"; import Store from "./stores/"; import Router from './routes'; interface Props {} const App = ({}: Props) => { return <Router />; }; ReactDOM.render( <React.StrictMode> <Provider store={Store}> <App /> </Provider> </React.StrictMode>, document.getElementById('root') );
# stores/index.ts import { combineReducers } from "redux"; import { configureStore } from "@reduxjs/toolkit"; import authReducer from "./auth"; const reducer = combineReducers({ auth: authReducer }); const Store = configureStore({ reducer }); export type RootState = ReturnType<typeof reducer> export type AppDispatch = typeof Store.dispatch export default Store;

試したこと

Redux Toolkitの公式を参照しつつ、以下の記事と動画を参考にさせていただきながら実装いたしました。

補足情報(FW/ツールのバージョンなど)

関連モジュールのバージョンは以下の通りです。

"react": "^16.13.1", "react-dom": "^16.13.1", "react-redux": "^7.2.0", "react-router": "^5.2.0", "react-router-dom": "^5.2.0" "react-scripts": "3.4.1", "redux": "^4.0.5", "@reduxjs/toolkit": "^1.3.6", "typescript": "~3.7.2"

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

問題が 2 点あります。

  • pages/SignUp/index.tsx で import された googleSignup は action creator (action を生成する関数) なので、dispatch には googleSignup そのものではなく、googleSignup を呼び出した結果を渡す必要があります。(この際、'fuga' を引数にすると良いのでは。) また、dispatch は同期的に処理されるようですが、hoge という変数が勝手に更新されることはありません。

  • stores/auth.ts で reducers: に指定している googleSignup は reducer の掟に従う必要があります。つまり、引数は state と action で、戻り値は更新された state (または元の state を変更せずにそのまま返す) であり、引数で渡された state そのものは絶対に変更してはいけません。

とりあえず、こんな感じに書き換えたら動くんじゃないでしょうか。

pages/SignUp/index.tsx

diff

1 function handleSubmit() { 2 console.log('Initial state: ' + hoge) 3- dispatch(googleSignup) 4+ dispatch(googleSignup('fuga')) 5 console.log('Update state: ' + hoge) 6 } 7 8 return ( 9 <div> 10 <h1>SignUp</h1> 11+ <p>Auth name: {hoge}</p> 12 <button onClick={handleSubmit}>Google Sign-up</button> 13 </div> 14 )

stores/auth.ts

diff

1-import { createSlice } from '@reduxjs/toolkit'; 2+import { createSlice, PayloadAction } from '@reduxjs/toolkit'; 3 4 // 略 5 6 const slice = createSlice({ 7 name: 'auth', 8 initialState, 9 reducers: { 10- googleSignup(state: State) { 11- state.name = 'fuga' 12- console.log('Action:' + state.name) 13+ googleSignup(state: State, action: PayloadAction<string>) { 14+ console.log('Action:', action) 15+ return { ...state, name: action.payload }; 16 } 17 } 18 });

ついでなので、createSlice が何をしているか、自分の推測したことを書いておきます。

js

1const slice = createSlice({ 2 name: 'auth', 3 initialState, 4 reducers: { 5 googleSignup(state: State, action: PayloadAction<string>) { 6 console.log('Action:', action) 7 return { ...state, name: action.payload }; 8 } 9 } 10});

戻り値 slice は actions と reducer を持ち、

  • slice.actions は reducers で指定された各 reducer に対応する action creator の集まりで、reducer の action の型を PayloadAction<T> にすると、対応する action creator の引数の型は T で、戻り値 (生成される action) の型は { type: string, payload: T } になる。また、action の type は createSlice に渡した name + '/' + reducer の名前 (上記の例では 'auth/googleSignup') になるっぽい。

  • slice.reducer は reducers をまとめたもので、state の初期値として initialState を返し、type が createSlice に渡した name + '/' + reducer の名前だったら各 reducer を呼び出すものと思われます。(このため、一つの action type で複数の reducer を反応させることはできない気がします。)

投稿2020/05/27 20:17

編集2020/05/27 20:47
hoshi-takanori

総合スコア7901

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

haku-bot

2020/05/28 09:18

大変ご丁寧に頂きまして本当にありがとうございました。 action creator と reducerそのものの仕様について理解不足でございました。 ご教示頂いたようにdispatchを通じてgoogleSignupの引数(action)に渡した値がpayloadにて変化、動くことが確認できました。 Reduxの理解が大変進みました。 またcreateSliceにつきましてもお知恵を頂きありがとうございます。 Redux toolkitは理解がしやすかっただけに、逆に内部でどのような処理が行われReduxが実現されているのかを意識して設計して参ります。 まだ本丸のAuthの状態管理はこれからでございますが、またつまずきましたらお知恵をお貸しいただけたら幸いです。 この度はありがとうございました。
guest

0

Router がわからなかったので、コードの全貌を見てませんが、フックのルール により、
内部関数で呼び出すことは出来ません。

フックを呼び出すのはトップレベルのみ
フックをループや条件分岐、あるいはネストされた関数内で呼び出してはいけません。

代わりに、あなたの React の関数のトップレベルでのみ呼び出してください。

eslint のプラグインを設定すれば、このようなルール外のフックの利用を検出できます。


追記: 回答が的外れであった為、訂正します。フック関数に対する勘違いでした。

投稿2020/05/27 18:42

編集2020/05/28 01:13
teamikl

総合スコア8760

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hoshi-takanori

2020/05/27 20:22

横から失礼します。フックのルールに従う必要があるのは use で始まる名前のついた関数 (useDispatch や useSelector) で、これらはルールに従って呼び出されていると思います。dispatch はフックではないので、SignUp の内部関数 handleSubmit で呼んでも問題ないはずです。(useState の戻り値である setXXX を内部関数で呼んでいいのと同様です。)
teamikl

2020/05/28 01:09

コメント指摘ありがとうございます。 >useState の戻り値である setXXX を内部関数で呼んでいいのと同様です。 で気がついた、私の勘違いでした。eslintの警告も出ませんね 誤情報すみません、回答を訂正します。
haku-bot

2020/05/28 09:19

teamikiさん、ありがとうございます。 hoshi-takanoriさん、補足を頂きありがとうございました。 フック関数はトップレベルでしか使えないことも意識をしておりませんでしたので、今後の知識となりました。 引き続き、どうぞよろしくお願いいたします!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問