React useReducer:收到有效负载,状态未更新

时间:2021-01-08 19:01:39

标签: reactjs leaflet react-hooks use-reducer leaflet-routing-machine

我使用这个 article 来帮助使用 useReducer 钩子。

我创建了这个上下文组件,因为我正在使用 react-leaflet 路由机器来为路线创建标记,我想保存纬度和< strong>longitude 在本地存储中;我最初可以保存它们,但无法更新它们!

基本上在我的 UserContext 组件中,我创建了这个 reducer 函数:

function userReducer(state, { type, payload }) {
    switch (type) {
      case 'isLengthOfMarkersLessThanTwoFalse': {
        return {
          ...state,
          isLengthOfMarkersLessThanTwo: payload.isLengthOfMarkersLessThanTwo
        };
      }
      case 'updateMarkers': {
        console.log('type, payload ', type, payload);
        return {
          ...state,

          ...state.markers.map(element => {
            console.log('element.alt === payload.alt ', element.alt === payload.alt);
            return element.alt === payload.alt ? { ...element, ...payload } : element;
          })
        };
      }
      default: {
        throw new Error(`Unhandled action type: ${type}`);
      }
    }
  }

所以你可以看到 case 子句中有一个 console.log

  case 'updateMarkers': {
            console.log('type, payload ', type, payload);
            return {
              ...state,

type, payload  updateMarkers LatLng {lat: 40.74154270838886, lng: -73.76230053137989, alt: "current location"} alt: "current location"lat: 40.74154270838886lng: -73.76230053137989

所以它实际上IS在reducer中被传递了,但是状态没有被更新!

This is the complete component :


import React, { useState, useEffect, useReducer } from 'react';
import { getUserAvatar } from '../../utils/index';

import { parse, stringify } from 'flatted';

var initialState = {
  avatar: '/static/uploads/profile-avatars/placeholder.jpg',
  isRoutingVisible: false,
  removeRoutingMachine: false,
  isLengthOfMarkersLessThanTwo: true,
  markers: [],
  currentMap: {}
};

var UserContext = React.createContext();

function setLocalStorage(key, value) {
  function isJson(item) {
    item = typeof item !== 'string' ? JSON.stringify(item) : item;

    try {
      item = JSON.parse(item);
    } catch (e) {
      return false;
    }

    if (typeof item === 'object' && item !== null) {
      return true;
    }

    return false;
  }

  try {
    window.localStorage.setItem(key, JSON.stringify(value));
  } catch (errors) {
    // catch possible errors:
    console.log(errors);
  }
}

function getLocalStorage(key, initialValue) {
  try {
    const value = window.localStorage.getItem(key);
    return value ? JSON.parse(value) : initialValue;
  } catch (e) {
    return initialValue;
  }
}

function UserProvider({ children }) {
  const [user, setUser] = useState(() => getLocalStorage('user', initialState));
  const [isAvatarUploading, setIsAvatarUploading] = useState(true);

  function userReducer(state, { type, payload }) {
    switch (type) {
      case 'isLengthOfMarkersLessThanTwoFalse': {
        return {
          ...state,
          isLengthOfMarkersLessThanTwo: payload.isLengthOfMarkersLessThanTwo
        };
      }
      case 'updateMarkers': {
        console.log('type, payload ', type, payload);
        return {
          ...state,

          ...state.markers.map(element => {
            console.log('element.alt === payload.alt ', element.alt === payload.alt);
            return element.alt === payload.alt ? { ...element, ...payload } : element;
          })
        };
      }
      default: {
        throw new Error(`Unhandled action type: ${type}`);
      }
    }
  }

  const [state, dispatch] = useReducer(userReducer, initialState);

  // console.log('user ', user);
  useEffect(() => {
    setLocalStorage('user', user);
  }, [user]);

  useEffect(() => {
    console.log('user.isRoutingVisibile ', user.isRoutingVisibile);
  }, [user.isRoutingVisibile]);

  useEffect(() => {
    console.log('state', state);
    if (user.markers.length === 2) {
      dispatch({
        type: 'isLengthOfMarkersLessThanTwoFalse',
        payload: { isLengthOfMarkersLessThanTwo: false }
      });
    }
  }, [JSON.stringify(user.markers)]);

  useEffect(() => {
    if (user.id) {
      getUserAvatar()
        .then(userAvatar => {
          setIsAvatarUploading(false);
          setUser(user => ({ ...user, avatar: userAvatar }));
        })
        .catch(err => console.log('error thrown from getUserAvatar', err));
    } else {
      console.log('No user yet!');
    }
  }, [user.id]);

  return (
    <UserContext.Provider
      value={{
        userId: user.id,

        setUserId: id => setUser({ ...user, id }),

        userAvatar: user.avatar,

        setUserAvatar: avatar => setUser({ ...user, avatar }),

        isAvatarUploading: isAvatarUploading,

        userImages: user.images,

        setUserImages: images => setUser({ ...user, images }),

        userMarkers: user.markers,

        setUserMarkers: marker => {
          state.isLengthOfMarkersLessThanTwo
            ? setUser(user => ({
                ...user,
                markers: [...user.markers, marker]
              }))
            : () => null;
        },

        setUpdateUserMarker: dispatch,

        deleteUserMarkers: () => {
          setUser({
            ...user,
            markers: [
              ...user.markers.filter(function(e, i, a) {
                return e !== a[a.length - 1];
              })
            ]
          });
        },

        setUserMarkersToNull: () =>
          setUser({
            ...user,
            markers: null
          }),

        userMap: user.currentMap,

        setUserCurrentMap: map =>
          setUser({ ...user, currentMap: { ...user.currentMap, map } }),

        removeRoutingMachine: user.removeRoutingMachine,

        resetUserMarkers: () => {
          console.log('fired setIsRoutingVisibileToTrue');
          setUser({
            ...user,
            removeRoutingMachine: true,
            isRoutingVisible: false,

            markers: []
          });
        },

        isRoutingVisibile: user.isRoutingVisible,

        setIsRoutingVisibileToTrue: () => {
          console.log('fired setIsRoutingVisibileToTrue');
          setUser({
            ...user,
            isRoutingVisible: true
          });
        },

        setIsRoutingVisibileToFalse: () => {
          console.log('fired setIsRoutingVisibileToFalse');
          setUser({
            ...user,
            isRoutingVisible: false
          });
        }
      }}
    >
      {children}
    </UserContext.Provider>
  );
}

export default UserContext;

export { UserProvider };

在路由机器中,我是这样称呼它的:

  setUpdateUserMarker({
    type: 'updateMarkers',
    payload: e.latlng
  });

1 个答案:

答案 0 :(得分:1)

在您的 case 'updateMarkers' 中,您正在映射 state.markers 数组,但您将其作为状态的顶级属性进行传播。这实际上将在您的状态上创建属性 01 等,并且根本不会更新 markers 属性。

运行这个非常简化的版本,看看它做了什么。

const state = {markers: [1,2,3]};
console.log({...state, ...state.markers});

想要做的是用映射数组替换 markers 属性。

case 'updateMarkers': {
  return {
    ...state,
    markers: state.markers.map(element => {
      return element.alt === payload.alt ? { ...element, ...payload } : element;
    })
  };
}