🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Redux

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

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

TypeScript

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

React.js

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

Q&A

解決済

1回答

2387閲覧

React|同じコードが2回実行され、データの取得が1回目と2回目で違う正確でない。

AkiUnleash

総合スコア1

Redux

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

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

TypeScript

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

React.js

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

0グッド

0クリップ

投稿2021/03/18 09:11

前提・実現したいこと

Reduxで値を管理しています。状態管理をしているデータをuseSelectorで取得したい。

発生している問題

useSelectorでデータを取得しようとすると二度同じコードが読まれます。
一度はカラのデータが返されて、二度目に実行され保存されているデータが取得されます。

イメージ説明

環境

package.json

{ "name": "react-typescript-sass-webpack", "version": "0.1.0", "private": true, "dependencies": { "@reduxjs/toolkit": "^1.5.0", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.5.0", "@testing-library/user-event": "^7.2.1", "@types/jest": "^24.9.1", "@types/node": "^12.20.6", "@types/react": "^16.14.5", "@types/react-dom": "^16.9.11", "@types/react-redux": "^7.1.16", "@types/react-router-dom": "^5.1.7", "react": "^17.0.1", "react-dom": "^17.0.1", "react-redux": "^7.2.2", "react-router-dom": "^5.2.0", "react-scripts": "4.0.3", "typescript": "^3.8.3" }, "scripts": { "start": "webpack serve", "build": "webpack" }, "eslintConfig": { "extends": "react-app" }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] }, "devDependencies": { "css-loader": "^5.1.3", "html-webpack-plugin": "^5.3.1", "mini-css-extract-plugin": "^1.3.9", "sass": "^1.32.8", "sass-loader": "^11.0.1", "style-loader": "^2.0.0", "ts-loader": "^8.0.18", "webpack": "^5.26.2", "webpack-cli": "^4.5.0", "webpack-dev-server": "^3.11.2" } }

該当のソースコード

webpack.config.js

const path = require('path') // const MODE = "production"; const MODE = "development"; const enabledSourceMap = MODE === "development"; // plugin const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { mode: MODE, entry: "./src/main.tsx", output: { path: `${__dirname}/dist`, filename: "main.js", }, devServer: { contentBase: path.resolve(__dirname, 'dist'), publicPath: '/dist/', port: 3040, open: true, historyApiFallback: true, // without no routing }, module: { rules: [ { test: /.scss/, use: [ "style-loader", { loader: "css-loader?modules", options: { url: true, sourceMap: enabledSourceMap, importLoaders: 2, }, }, { loader: "sass-loader", options: { sourceMap: enabledSourceMap, }, }, ], }, { test: /.(gif|png|jpg|eot|wof|woff|ttf|svg)$/, type: "asset/inline", }, { test: /.tsx?$/, use: "ts-loader" } ] }, resolve: { extensions: [".ts", ".tsx", ".js", ".json"] }, target: ["web", "es5"], plugins: [ new HtmlWebpackPlugin({ publicPath: '', filename: 'index.html', template: 'src/html/index.html', }), ] };

tsconfig.json

{ "compilerOptions": { "sourceMap": true, "noImplicitAny": true, "allowJs": true, "strictNullChecks": true, "module": "ES6", "target": "ES2015", "jsx": "react", "allowSyntheticDefaultImports": true, "moduleResolution": "node", }, "include": [ "src", ], "exclude": [ "node_modules" ], }

main.tsx

// React import React, { useEffect } from 'react'; import * as ReactDOM from 'react-dom'; import { BrowserRouter, Switch, Route } from 'react-router-dom'; // Components import Top from './components/pages/Top'; import { login } from './common/redux/userSlice' // Redux import { Provider } from "react-redux" import { store } from './common/redux/store' import { useDispatch } from 'react-redux' const App: React.FC = () => { const dispatch = useDispatch(); useEffect(() => { dispatch( login({ uid: "test_uid", photoUrl: "test_phtho", displayName: "test_displayname", })) }, [dispatch]); return ( <BrowserRouter> <Switch> <Route exact path="/" component={Top} /> </Switch> </BrowserRouter> ); } ReactDOM.render( <React.StrictMode> <Provider store={store}> <App /> </Provider> </React.StrictMode>, document.querySelector('#app'));

Top.tsx

import React from 'react'; // Componens import Footer from '../organisms/Footer'; import Header from '../organisms/Header'; // assets import style from '../../assets/scss/style.module.scss' // redux import { selectUser } from '../../common/redux/userSlice' import { useSelector } from 'react-redux' const Top: React.FC = () => { // 上記の画像はここのコード。二度読まれ、一度目は値を取得できていない。 const user = useSelector(selectUser); console.log(user.uid); return ( <> <Header /> <div className={style['title']}> トップページ </div> <Footer /> </> ); }; export default Top;

store.tsx

import { configureStore, ThunkAction, Action } from "@reduxjs/toolkit"; import userReducer from './userSlice' export const store = configureStore({ reducer: { user: userReducer, } }) export type RootState = ReturnType<typeof store.getState> export type AppThunk<ReturnType = void> = ThunkAction< ReturnType, RootState, unknown, Action<string> >; // export type AppDispatch = typeof store.dispatch;

userSlice.tsx

import { createSlice, PayloadAction } from "@reduxjs/toolkit" import { RootState } from "./store" interface USER { displayName: string; photoUrl: string; } export const userSlice = createSlice({ name: "user", initialState: { user: { uid: "", photoUrl: "", displayName: "" }, }, reducers: { login: (state, action) => { state.user = action.payload; }, logout: (state) => { state.user = { uid: "", photoUrl: "", displayName: "" } }, updateUserProfile: (state, action: PayloadAction<USER>) => { state.user.displayName = action.payload.displayName; state.user.photoUrl = action.payload.photoUrl; } } }) export const { login, logout, updateUserProfile } = userSlice.actions; export const selectUser = (state: RootState) => state.user.user; export default userSlice.reducer;

試したこと

以下、試しましたが現象は変わりませんでした。

  • React.StrictModeを削除
  • BrowserRouterを止めてみた
  • Webpack4で試した。

宜しくお願いします。

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

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

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

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

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

guest

回答1

0

ベストアンサー

それが正常な動作です。useEffect で指定した関数はレンダーが終了した後に実行されるので、初回のレンダーでは useSelector は初期値 { uid: "", photoUrl: "", displayName: "" } を返し、その後 useEffect で store が更新された結果、新しい値を使って再度レンダーし直すことになります。

参考: useEffect (フック API リファレンス – React より)

投稿2021/03/18 16:58

編集2021/03/18 17:00
hoshi-takanori

総合スコア7899

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

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

AkiUnleash

2021/03/19 01:58

hoshi-takanori様、ご返答いただき有難うございました。 実際私が開発しているプログラムの話ですが、先に初期値(空白)の値を見るためエラーが発生してしまい困っていました。値を設定しているのに、空白が出てしまうのは何故だろうと…数日悩んでおりました。 今回で言うTopコンポーネントをレンダリングする前に、条件分岐でuser.uidの値があるときにレンダリングするように変更し解決しました。 この度はご対応いただき、本当に有難うございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問