When a stateful variable that's been instantiated using the useState()
React Hook in a Context Component I've built is updated (using a setState()
method), one of my functional components that consumes this variable doesn't update. Should this variable be consumed directly in the functional component, or passed as a prop from a parent functional component?
Expected Behavior
The user clicks a button, triggering the deletion of one of the notifications they've set. The UI then deletes that notification from the table row displaying in the UI. When there are no notifications, an option to add one is presented to the user. When the user then adds a notification, it appears in the UI as a table row.
Observed Behavior
The user is able to delete notifications (and this reflects in the table instantly), but when all notifications are deleted and the user attempts to add one, it is successfully added to by using a setState()
method in the context provider component, but this does not cause the functional component consuming this variable via the useContext()
method to update the UI.
Relevant Code Snippets
Context Component Instantiates the alert
Variable and Uses Reducer Function to Update It
import React, { createContext, useState } from 'react'
import { sampleUser } from '../SampleData'
export const SettingsContext = createContext();
const SettingsContextProvider = props => {
// Instantiates alerts array (via sample data), and establishes the setAlert update method
const [alerts, setAlerts] = useState(importedSampleUser.withoutAlert);
...
/** Reducer function that handles all notification modifications.
* @param {type} string Add, delete, personal, or organizational.
* @param {obj} obj Object containing the details needed to complete the action on the backend.
*/
const updateAlerts = (type, obj) => {
switch (type) {
// Creates an empty array if notificaitons have all been deleted
case "add notification":
if (!alerts.length) {
setAlerts([]);
};
let newAlertArray = alerts;
newAlertArray.push({
id: obj.id,
type: "Birthday",
group: obj.group,
hoursPrior: obj.hoursPrior
});
// Updates the variable consumed by the UI Component
setAlerts(newAlertArray);
break;
case "delete notification":
let withoutAlert = alerts;
withoutAlert = withoutAlert.filter(alert => alert.id !== obj.id);
setAlerts(withoutAlert);
break;
default:
console.log("Oops! No more alert update types available.");
return;
}
}
UI Component Builds Layout
const PersonalAlerts = () => {
// Holds basic layout
return (
<div>
<h5>Your Alerts</h5>
{/* Displays a table with a list of notifications the user has set */}
<AlertTable />
</div>
);
Child UI Component Creates a Table Based on alert
Variable Consumed from Context
const AlertTable = () => {
// Consumes the alerts state from the Context Component
const { alerts, updateAlerts } = useContext(SettingsContext);
// Handles personal alert delete requests.
const deleteAlert = (e, type, id) => {
e.preventDefault();
// Dispatches action to Settings Context
updateAlerts("delete personal", { id });
};
// Builds an element in the table for each alert the user has set.
let tableElements = alerts.map(alert => {
...
return (
<tr key={alert.id}>
{/* Builds alert row for each alert in array received from Context */}
<td>{alert.type}</td>
</tr>
);
});
// Builds a table row to display if there are no personal alerts to display.
const noAlerts = (
<tr>
<td onClick={() => triggerAddNotificationUI()}>Add a notificaiton</td>
</tr>
);
};
Questions
- Why, when the
alerts
stateful variable is updated in the Context Component, doesn't the UI--specifically, theAlertTable
Component--re-render? Should a re-render be triggered by a change in the variable consumed byuseContext()
? - Is it better practice to consume a Context variable directly in a UI Component (e.g.
AlertTable
), or to consume it "higher in the Component tree" (e.g.PersonalAlerts
), then pass it down as a prop to the child component?
Thanks in advance for your help.