Перехватчики реакции: useState / context; Невозможно прочитать свойство «аватар» неопределенного значения / Как обновить вложенный объект

Я передал переменную состояния и функцию из файла контекста:

UserContext:

import React, { useState } from 'react';

const UserContext = React.createContext();

function UserProvider({ children }) {
  var [userImages, setUserImages] = useState({
    avatar: '/static/uploads/profile-avatars/placeholder.jpg'
  });

  return (
    <UserContext.Provider
      value={{
        userImages,
        setUserImages
      }}
    >
      {children}
    </UserContext.Provider>
  );
}

export default UserContext;

export { UserProvider };

На данный момент UserImages - это просто объект с одной опорой, т.е. avatar

Это мое приложение, которое обернуто поставщиком (пожалуйста, не обращайте внимания на реализацию redux, я просто хотел попробовать Context)

import React from 'react';
import { Provider } from 'react-redux';
import { UserProvider } from './UserContext';
import App from 'next/app';
import withRedux from 'next-redux-wrapper';
import { PersistGate } from 'redux-persist/integration/react';

import reduxStore from '../store/index';

import withReactRouter from '../with-react-router/with-react-router';


class MyApp extends App {
  static async getInitialProps({ Component, ctx }) {
    const pageProps = Component.getInitialProps
      ? await Component.getInitialProps(ctx)
      : {};
    return { pageProps };
  }
  render() {
    const { Component, pageProps, store } = this.props;

    return (
      <UserProvider>
        <Provider store={store}>
          <PersistGate persistor={store.__PERSISTOR} loading={null}>
            <Component {...pageProps} />
          </PersistGate>
        </Provider>
      </UserProvider>
    );
  }
}

Я пытаюсь обновить некоторый контекст с помощью функции setState после этого сообщения

Однако я все еще получаю TypeError: Cannot read property 'avatar' of undefined

Это форма объекта состояния:

userData:
setUserImages: ƒ ()
userImages:
avatar: "/static/uploads/profile-avatars/placeholder.jpg"

or

userData : {
  setUserImages : SetUserImages function,
  userImages : {
  avatar : "/static/uploads/profile-avatars/placeholder.jpg"
  }
 }

Мой компонент:

 function ImageUploader({ userData }) {

  var { avatar } = userData.userImages;
  var setUserAvatar = userData.setUserImages;

  function avatarUpdater(avatarPath) {
    setUserAvatar({ userData: { ...userData.userImages.avatar, avatarPath } });
    }
  }

Кто-нибудь знает, почему это происходит?


person Antonio Pavicevac-Ortiz    schedule 10.06.2020    source источник
comment
Возможно, вам придется проверить userData и userData.userImages, прежде чем получать avatar от него. Откуда userData ? Как этот вопрос связан с response-context?   -  person Tony Nguyen    schedule 10.06.2020
comment
@TonyNguyen Вы правы, возможно, мне стоит обновить свой вопрос, я добавил некоторый контекст ...   -  person Antonio Pavicevac-Ortiz    schedule 10.06.2020
comment
Где вы используете ImageUploader?   -  person Vivek Doshi    schedule 10.06.2020


Ответы (2)


UserProvider - это корень вашего приложения, поэтому вы можете напрямую {userImages, setUserImages} получить его в ImageUploader

function ImageUploader() {
  const {userImages, setUserImages} = useContext(UserContext)
  const { avatar } = userImages;

  function avatarUpdater(avatarPath) {
     setUserImages({ avatar: avatarPath });
  }
}

person Tony Nguyen    schedule 10.06.2020

Обычно рекомендуется не раскрывать setState из вашего контекста. Вы хотите обернуть его явным методом для обновления состояния, а затем добавить этот метод в свой поставщик. Что-то типа:

const userContext = {
  avatar: userImages,
  updateAvatarUrl: (url) => {
    setUserImages(prevState => ({...prevState, avatar: url}))
  }
}

return <UserContext.Provider value={userContext}>{children}</UserContext.Provider>

Попробуйте добавить ловушку для своего UserContext, которую вы можете использовать внутри своего компонента.

В UserContext добавить

export const useUserContext = () => useContext(UserContext)

Затем внутри вашего компонента:

import { useUserContext } from '<UserContext import>'

...

function avatarUpdater(avatarPath) {
    userCtx.updateAvatarUrl(avatarPath)
}

На мой взгляд, самая чистая структура для Context. Позволяет более точно контролировать состояние контекста.

person jmkmay    schedule 10.06.2020