简体   繁体   中英

React hook state - does not update component

So I'm running an app with the following setup:

  • Hook - provides state and setter to update the state
  • Component A - only uses the state from the hook and displays data from it
  • Component B - only uses the setter and updates the state of the hooks state

However with my current setup Component A does not rerender when the hook state does get a new item in the array, any ideas why this is happening? Im providing some code for clearification:

Hook

const initialValue = [];

function getLocalStorageItem() {
  const item = window.localStorage.getItem("queries");
  return item ? JSON.parse(item) : initialValue;
}

function useLocalStorageQueryHistory() {
  const { dispatch } = useAlertContext();

  const [recentQueries, setRecentQueries] = useState(() => {
    try {
      return getLocalStorageItem();
    } catch (error) {
      dispatch(receiveMessageInterceptor(error));
      return initialValue;
    }
  });

  const setValue = (value) => {
    try {
      const recentQueries = getLocalStorageItem();
      if (recentQueries.length >= 6) {
        recentQueries.shift();
      }

      if (!recentQueries.some((query) => query.params === value.params)) {
        window.localStorage.setItem(
          "queries",
          JSON.stringify([...recentQueries, value])
        );
        setRecentQueries([...recentQueries, value]);
      }
    } catch (error) {
      dispatch(receiveMessageInterceptor(error));
    }
  };

  return { recentQueries, setValue };
}

Component A

function RecentQueriesContainer() {
  const { recentQueries } = useLocalStorageQueryHistory();

  return (
    <Container disableGutters>
        {recentQueries.length ? (
          recentQueries.map((item) => (
            <Card key={`${item.params}`}>
              <CardHeader
                title={item.description}
              />
                  <Typography variant={"body2"}>
                    Mode: {item.params.split("&visualization=")[1]}
                  </Typography>
                  <Typography variant={"body2"}>Unit: {item.unit}</Typography>
            </Card>
          ))
        ) : (
          <Typography
            variant={"subtitle2"}
            color={"secondary"}
            align={"center"}
            mt={2}
          >
            No recent queries available
          </Typography>
        )}
    </Container>
  );
}

Component B

Simply uses the setter in useEffect

useEffect(() => {
    const {
      features,
      description,
      unit,
      amountOfCountries,
    } = geoJsonFromSelectedStatistic;
    if (features) {
      setValue({
        description,
        unit,
        amountOfCountries,
        params: window.location.search,
      });
    }
  }, [geoJsonFromSelectedStatistic]);

I believe that is because you're not creating a context. Hooks don't share state by default, they only share state logic .

So component A and B are using the same hook, but the state between them is different.

Try creating a context and then using that to share the state. It should work fine:)

const AuthContext = createContext({});

export const AuthProvider = ({ children }) => {
  const [isAuthenticated, setIsAuthenticated] = useState();

  return (
    <AuthContext.Provider value={{ isAuthenticated, setIsAuthenticated }}>
      {children}
    </AuthContext.Provider>
  );
};

export function useAuth(){
  const context = useContext(AuthContext);

  if (!context) {
    throw new Error('useAuth must be used within an AuthProvider');
  }
  return context;
}

Something like this, where AuthProvider acts like your custom hook, whose values are the values exported by your hook. These will be available for any component in which the context is used with useAuth

Don't forget to wrap your app with the <AuthProvider />

I followed the approach of @thales-kenne and used the react context I already had a UI-context set up and extended it as follows:

UI-context.js

const initialState = {
  sidebarOpen: false,
  ...
  recentQueries: getLocalStorageItem(),
};

function uiReducer(state = initialState, action) {
  switch (action.type) {
    case SIDEBAR: {
      return {
        ...state,
        sidebarOpen: action.sidebarOpen,
      };
    }
    ...
    case RECENT_QUERIES: {
      return {
        ...state,
        recentQueries: action.recentQueries,
      };
    }
    default:
      return state;
  }
}

UI-actions.js

export function getLocalStorageItem() {
  const initialValue = [];
  try {
    const item = window.localStorage.getItem("queries");
    return item ? JSON.parse(item) : initialValue;
  } catch (error) {
    console.error(error);
    return initialValue;
  }
}

export const setRecentQueries = (value) => {
  try {
    const recentQueries = getLocalStorageItem();
    if (recentQueries.length >= 5) {
      recentQueries.shift();
    }

    if (!recentQueries.some((query) => query.uri === value.uri)) {
      window.localStorage.setItem(
        "queries",
        JSON.stringify([...recentQueries, value])
      );
      return {
        type: RECENT_QUERIES,
        recentQueries: [...recentQueries, value],
      };
    }
  } catch (error) {
    console.error(error);
  }
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM