Search code examples
javascriptreactjsreact-hooksreact-contextreact-state-management

React state property undefined in function component using Context API


I am new to React's context API and hooks for function components. I am trying to pass state to a child component, ActivityTable.js. I wrapped the provider around the app (App.js), however state properties are still undefined in ActivityTable.js -- TypeError: Cannot read property 'id' of undefined.

Any guidance would be appreciated.


App.js

import ActivityState from "./context/activities/ActivityState";

const App = () => {
  return (
    <StylesProvider injectFirst>
      <ContactState>
        <ActivityState>
          ...
        </ActivityState>
      </ContactState>
    </StylesProvider>
  );
};

export default App;

ActivityState.js

import React, { useReducer } from 'react';
import ActivityContext from './ActivityContext';
import ActivityReducer from './ActivityReducer';
import { ADD_ACTIVITY, DELETE_ACTIVITY, SET_CURRENT_ACTIVITY } from '../types';

const ActivityState = props => {

    const initialState = {
        activities: [
            {
                id: 1,
                activity_description: "a desc",
                activity_name: "a",
            },
            {
                id: 2,
                activity_description: "b desc",
                activity_name: "b",
            },
            {
                id: 3,
                activity_description: "c desc",
                activity_name: "c",
            }
        ]
    };

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

    const deleteActivity = id => {
        dispatch({ type: DELETE_ACTIVITY, payload: id });
    };

    const setCurrentActivity = activity => {
        dispatch({ type: SET_CURRENT_ACTIVITY, payload: activity });
    };

    return ( 
        <ActivityContext.Provider 
            value={{
                    activities: state.activities, 
                    deleteActivity,
                    setCurrentActivity
                }}>
           { props.children }
        </ActivityContext.Provider>
    );
}

export default ActivityState;

ActivityContext.js

import { createContext } from "react";

const ActivityContext = createContext(null);

export default ActivityContext;

ActivityReducer.js

import { DELETE_ACTIVITY, SET_CURRENT_ACTIVITY } from '../types';

  export default (state, action) => {
    switch (action.type) {
      case DELETE_ACTIVITY:
        return {
          ...state,
          activities: state.activities.filter(
            activity => activity.id !== action.payload
          )
        };
      case SET_CURRENT_ACTIVITY:
        return {
          ...state,
          current: action.payload
        };
      default:
        return state;
    }
  };

ActivityView.js

import React, { useContext } from "react";

import ActivityContext from '../../context/activities/ActivityContext';

import ActivityTable from './ActivityTable';

const Activities = () => {
    const activityContext = useContext(ActivityContext);

    const { activities } = activityContext;

    console.log('activities: ', activities);

    return (
        <div>
           <ActivityTable/>
        </div>
    );
}

export default Activities;

ActivityTable.js

import React, { useContext, useState } from "react";
import ActivityContext from "../../context/activities/ActivityContext";

const ActivityTable = ({ activity }) => { //activity is undefined here
    const activityContext = useContext(ActivityContext);

    const { activities } = activityContext;

    const { id, activity_name, activity_desc } = activity; //undefined

    return (
        <div>
            <tr>
                <td>{id}</td>
                <td>{activity_name}</td>
                <td>{activity_desc}</td>
            </tr>
        </div>
    );
};

export default ActivityTable;

Solution

  • It looks like you're using activity as a prop inside ActivityTable, but never actually supplying that prop.

    <ActivityTable activity={foo} />
    

    I can't tell what data you're trying to pass to the table. You're importing the context successfully in both components, but never using the context data.