Проблема с сохранением данных в состояние - PullRequest
1 голос
/ 16 апреля 2020

У меня есть useEffect, который устанавливает state.user, который позволяет мне сохранять состояние и сохранять пользователя во время навигации. Auth.currentSession и Auth.currentAuthenticatedUser (строка 105-106) являются частью библиотеки AWS Amplify и в основном извлекают из localStorage (о котором я только что узнал вчера). Так что useEffect запускает, вызывает диспетчеризацию в строке 110. Строка 115 печатает возвращенные данные из Auth.currentAuthenticatedUser, но 116 печатает что-то, эквивалентное initialState, когда я ожидаю увидеть значения, эквивалентные «user», потому что «user» отправляется как полезная нагрузка с отправка в «ЛОГИН». Я предполагаю, что аргумент «[]» в useEffect имеет какое-то отношение к нему, но я не могу понять, какое значение поместить туда, чтобы оно не перешло в бесконечное число l oop. Моя главная цель - сохранить данные в состояние и использовать их в других компонентах с помощью useContext.

import React, { useEffect } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import './App.css';
import MainNavbar from './components/Navbar';
import Home from './components/Home';
import LogIn from './components/auth/LogIn';
// import Register from './components/auth/Register';
import ForgotPassword from './components/auth/ForgotPassword';
import ForgotPasswordVerification from './components/auth/ForgotPasswordVerification';
import ChangePassword from './components/auth/ChangePassword';
import ChangePasswordConfirm from './components/auth/ChangePasswordConfirm';
// import Welcome from './components/auth/Welcome';
// import Ingredients from './components/Ingredients';
import Recipes from './components/Recipes';
import Footer from './components/Footer';
import { Auth } from 'aws-amplify';
// import axios from 'axios';
import { library } from '@fortawesome/fontawesome-svg-core';
import { faCheckSquare, faCoffee, faEdit } from '@fortawesome/free-solid-svg-icons';

library.add( faCheckSquare, faCoffee, faEdit);

// const config = require('./config.json');

export const AuthContext = React.createContext();
const initialState = {
  isAuthenticated: false,
  // isAuthenticating: true,
  user: null,
  userSettings: null, 
  userRec: null,
  userIng: null, 
  userData: null,
  defaultIng: null
}
const reducer = (state, action) => {
  switch (action.type) {
    case "LOGIN":
      console.log("LOGIN");
      return {
        ...state,
        isAuthenticated: true,
        user: action.payload
      };
    case "LOGOUT":
      console.log("LOGOUT");
      return {
        ...state,
        isAuthenticated: false,
        user: null
      };
    case "INITIALIZE_USER_DATA":
      const userId = state.user.attributes.sub;
      console.log(state.user);
      let userRec = [];
      let userIng = [];
      let userData = [];
      let defaultIng = [];
      async function initializeUserData() {
        try {
          const params = {
            "userId": userId,
            "sett": true
          };
          let res1 = await axios.patch(`${config.api.invokeUrl}/user/configsettings`, params);
          let res2 = await axios.post(`${config.api.invokeUrl}/recipes/${userId}`, params);
          let res3 = await axios.get(`${config.api.invokeUrl}/ingredients`);
          console.log(res1);
          console.log(res2);
          console.log(res3);
          defaultIng = res3.data.sort((a, b) => (a.iName > b.iName) ? 1 : -1);
          // this.setUserSettings(res1.data.Attributes);  //4-13-2020, Myles - seems to be the same data coming from res2 and assigned to userData.
          let arr1 = res2.data;
          arr1.forEach( item => {
            if (item.sk.startsWith("USER-")) {
              userData.push(item);
            } else if (item.sk.startsWith("REC-")) {
              userRec.push(item);
            } else if (item.sk.startsWith("ING-")) {
              userIng.push(item);
            }
          });
        } catch (error) {
          console.log(error);
        }
      }
      initializeUserData();
      return {
        ...state,
        isAuthenticated: true,
        user: action.payload,
        userRec: userRec,
        userIng: userIng,
        userData: userData,
        defaultIng: defaultIng
      };
    default:
      return state;
  }
};
function App() {
  const [state, dispatch] = React.useReducer(reducer, initialState);
  async function authStatus() {
    try {
      let session = await Auth.currentSession();
      let user = await Auth.currentAuthenticatedUser();
      console.log(user);
      console.log(session);
      if (session && user) {
        dispatch({
          type: "LOGIN",
          payload: user
        });
      };
      console.log(user);
      console.log(state);
    }catch(error) {
      console.log(error);
    }
  };
  useEffect(() => {
    authStatus();
  },[]);
  return (
    <AuthContext.Provider
      value={{
        state,
        dispatch
      }}
    >
      <div className="App">
        <Router>
          <div className="container">
            <MainNavbar />
            <Switch>
              <Route exact path="/" component = {Home} />
              <Route exact path="/login" render={() => <LogIn />} />
              <Route exact path="/recipes" render={() => <Recipes />} />
              <Route exact path="/forgotpassword" render={() => <ForgotPassword />} />
              <Route exact path="/forgotpasswordverification" render={() => <ForgotPasswordVerification />} />
              <Route exact path="/changepassword" render={() => <ChangePassword />} />
              <Route exact path="/changepasswordconfirmation" render={() => <ChangePasswordConfirm />} />
            </Switch>
            <Footer />
          </div>
        </Router>
      </div>
    </AuthContext.Provider>
  );
}
export default App;

1 Ответ

0 голосов
/ 27 апреля 2020

Я предполагаю, что аргумент [] в useEffect как-то связан с этим (состояние всегда initialState) , но я не могу понять, что value поставить туда, чтобы он не входил в бесконечность l oop !!

Общий вопрос, который насмехается над многими из нас при использовании знаменитого useEffect крючка. Действительно, useEffect имеет какое-то отношение к вашим state обновлениям, если какие-либо побочные эффекты действительно изменят ваш state.

Примечание: Если вы передадите пустой массив [], реквизиты и состояния внутри эффекта всегда будут иметь свои начальные значения.

Быстрое решение: (+)

  • Либо добавьте state в массив зависимостей useEffect

  • или console.log(state) в операторе return()

// summarized version, to focus on issue at hand
function App() {
  const [state, dispatch] = React.useReducer(reducer, initialState);

  // invoked within the useEffect()
  async function authStatus() {
    try {
      let session = await Auth.currentSession();
      let user = await Auth.currentAuthenticatedUser();
      if (session && user) { dispatch({ type: "LOGIN", payload: user }) };
      console.log(user);
      console.log(state); // thus, this is always initialState (see useEffect() below)
    } catch(error) {
      console.log(error);
    }
  };

  useEffect(() => {
    authStatus();
    // If you pass an empty array ([]), the props and state 
    // inside the effect will always have their initial values.
- }, []);
    // Either, refactor the dependency array to include "state"
+ }, [state]);

  return (
    <AuthContext.Provider value={{ state, dispatch }}>
      <div className="App">
+       {console.log(state)} // Or, log "state" here
        <Router>{/* Routes */}</Router>
      </div>
    </AuthContext.Provider>
  );
}
export default App;

Вот подробное объяснение: Благодарим команду React, которая хорошо объяснила useEffect () и его эффекты:)

  • Обратите внимание, что по умолчанию useEffect запускается после первый render и после каждого обновления. Иногда вы не хотите, чтобы useEffect всегда запускался после render. Как это сделать?

  • Таким образом, чтобы оптимизировать производительность, вы можете пропустить эффекты, передав array [] в качестве необязательного второго аргумента в useEffect. Опять же, команда React рекомендует использовать правило исчерпывающего действия как часть пакета eslint-plugin-реагировать-hooks . Он предупреждает, что зависимости указаны неверно, и предлагает исправление. Обратите внимание, что это включено по умолчанию, если вы используете create-реагировать-приложение .

  • Если вы используете эту оптимизацию (упомянутую выше), убедитесь, что массив включает в себя все значения из области компонента (например, реквизиты и состояние), которые изменяются со временем и используются эффектом. В противном случае ваш код будет ссылаться на устаревшие значения из предыдущих рендеров. Поэтому, если вы console.log() здесь, реквизит и состояние всегда будут иметь свои начальные значения.

...