2

I am new to React hooks/Context API. I have read the React hook/context docs, and I am still having trouble with the following:

  1. My attempts to update global state by multiple consumer components currently causes frequent overwriting of context state due to rerendering (e.g., activity or details state is sometimes null/undefined). This probably is why...
  2. ... I am getting 400 (bad request) and/or 500 (server) errors on random refreshes of the page (~30% of the time content loads as expected, ~70% errors are thrown. I believe this is happening because we have various context states that are being called asynchronously).
  3. I am not sure how to implement Axios Cancellation, given that our useEffect hooks are calling dispatch functions (e.g., getActivities()) in different files. The examples I've seen involve fetching data within the component (rather than in context).

I am seeking assistance for #1 specifically. I would love guidance on how to accurately fetch data and store in context as global state, and then provide that context to child components, allowing them to consume/update context state without unnecessary rerendering.

Tried to only provide relevant code snippets below:

ActivityState.js -- should fetch activity data

...
const ActivityState = props => {
  const initialState = {
    activities: [],
    isLoading: false,
    isError: false
  };

  const HEADERS = {
    'Content-Type': 'application/json',
    'user_id': 1
  }

  const [state, dispatch] = useReducer(ActivityReducer, initialState);
  const userContext = useContext(UserContext);

  const getActivities = async () => {
    const { loggedIn } = contactContext;
    let didCancel = false; // attempts to start implementing axios cancellation
    try {
      const res = await axios.get(url);
      dispatch({ type: GET_ACTIVITIES, payload: res.data.data.activities });
    } catch (err) {
      if (!didCancel) {
        dispatch({ type: 'FETCH_FAILURE' });
      }
    }
  }

  const updateActivity = (path, data) => { //update context state
    dispatch({ type: UPDATE_ACTIVITY, payload: { path: path, data: data } });
  };

  const saveActivity = () => { //send new activity data to the backend
    const postData = {
      actions:  [{"293939": []}],
      activities: state.activities
    };
    try {
      const res = axios.post(url,{ data: postData }, { headers: HEADERS });
    } catch (err) {
      console.log(err);
    }
  }

return (
    <ActivityContext.Provider
      value={{
        activities: state.activities,
        data: state.data,
        backup_data: state.backup_data,
        getActivities,
        updateActivity,
        saveActivity,
      }}
    >
      {props.children}
    </ActivityContext.Provider>
  );
};

export default ActivityState;

ActivityReducer.js -- switch statements to be dispatched by ActivityState.js

...
export default (state, action) => {
  switch (action.type) {
    case GET_ACTIVITIES:
      return {
        ...state,
        activities: action.payload,
        isLoading: true
      };
    case FETCH_FAILURE:
      return {
        ...state,
        isLoading: false,
        isError: true
      };
    case UPDATE_ACTIVITY:
      const { payload: { path }, payload } = action;
      const data = state;
      if (!data.activities)
        return { data };
      const index = data.activities.findIndex(e => e.socium_tracking_number == path.id);
      if(index === -1)
        return { data };
      _.set(data, `activities[${index}].${path.field}`, payload.data);
      return {
        data,
      };
...

DetailsState.js -- dispatch functions to fetch details

const DetailsState = props => {

    const initialState = {
        details: null,
    };

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

    const getDetails = async () => {
        try {
            const res = await axios.get(url);
            dispatch({ type: GET_DETAILS, payload: res.data.data[0].details});
        }catch(err) {
            console.log(err)
        }
    };

    return ( 
        <DetailsContext.Provider 
            value={{ details: state.details, getDetails }}
        >
            { props.children }
        </DetailsContext.Provider>
    );
}
export default SchemaState;

DetailsReducer.js -- switch statement

export default (state, action) => {
    switch (action.type) {
        case GET_DETAILS:
            return {
                ...state,
                details: action.payload,
            };
        default:
            return state;
    }
};

ActivityTable.js -- component that consumes Activity Info

...
const ActivityTable = ({ activity }) => {
  const activityContext = useContext(ActivityContext);
  const { activities, filtered, getActivities } = activityContext;

  const [order, setOrder] = React.useState('asc');
  const [orderBy, setOrderBy] = React.useState(activities.wait_time);

  // Get activity data on mount
  useEffect(() => {
      async function fetchData() {
        await getActivities()
      }
      fetchData();
  }, []);
...

CreateActivity.js -- component that consumes Activity and Details data

...
const CreateActivity = props => {
  const activityContext = useContext(ActivityContext);
  const { activities, filtered, getActivities, addActivity } = activityContext;

  const detailsContext = useContext(DetailsContext);
  const { details, getDetails } = detailsContext;

  // Get activity and details data on mount
  useEffect(() => {
     async function fetchData() {
       await getActivities();
       await getSchema();
     }
     fetchData();
  }, []);
...

I really tried to get smarter on these issues before approaching the SO community, so that my question(s) was more defined. But this is what I have. Happy to provide any info that I missed or clarify confusion. Thank you for your time

BWeb303
  • 303
  • 6
  • 18

0 Answers0