Following the React tutorial on combining reducers with Context, I have packaged up a reducer into a Provider
component:
export function CoursesModeProvider({ children, coursesDB }) {
const [courses, dispatch] = useReducer(
coursesModeReducer,
{ all: coursesDB, displayed: coursesDB, editing: null }
);
return (
<CoursesModeContext.Provider value={courses}>
<CoursesModeDispatchContext.Provider value={dispatch}>
{children}
</CoursesModeDispatchContext.Provider>
</CoursesModeContext.Provider>
);
}
But what if the parent component that needs to use this CoursesModeProvider
has complex state logic that involves ternary expressions? It appears that I then have to do something like this in the component's render code if I want the reducer to be accessible by child components:
return (
(showAddCourseDialog)
? <CoursesModeProvider coursesDB={coursesDB}>
<CoursesModeAdd close={closeAddCourseDialog} />
</CoursesModeProvider>
: (showCourseDetailsDialog)
? <CoursesModeProvider coursesDB={coursesDB}>
<CoursesModeDetails close={closeCourseDetailsDialog} />
</CoursesModeProvider>
: <CoursesModeProvider coursesDB={coursesDB}>
<h1 className="centered">Courses</h1>
<CoursesModeSearchFilter />
<CoursesModeTable showCourseDetails={openCourseDetailsDialog} />
<button className="float-btn" onClick={openAddCourseDialog}>
<FontAwesomeIcon icon="map-pin" />
Add Course
</button>
</CoursesModeProvider>
);
}
Ouch! Every conditional JSX block needs to be packaged in a <CoursesModeProvider>
! I am concerned that this is inefficient because a new reducer needs to be spun up on each state change.
An alternative appears to be not to package the context and reducer into a provider, and instead declare the reducer locally within the parent component. Then I think I could do something like this:
return (
<CoursesModeContext.Provider value={courses}>
<CoursesModeDispatchContext.Provider value={dispatch}>
(showAddCourseDialog)
? <CoursesModeAdd close={closeAddCourseDialog} />
: (showCourseDetailsDialog)
? <CoursesModeDetails close={closeCourseDetailsDialog} />
: <>
<h1 className="centered">Courses</h1>
<CoursesModeSearchFilter />
<CoursesModeTable showCourseDetails={openCourseDetailsDialog} />
<button className="float-btn" onClick={openAddCourseDialog}>
<FontAwesomeIcon icon="map-pin" />
Add Course
</button>
</>
</CoursesModeDispatchContext.Provider>
</CoursesModeContext.Provider>
);
Which is preferrable and why?
It's not necessary to repeat the CoursesModeProvider
component in each branch. You can use the ternary operator to choose only the children for the component.
return (
<CoursesModeProvider coursesDB={coursesDB}>
{showAddCourseDialog ? <CoursesModeAdd close={closeAddCourseDialog} /> :
showCourseDetailsDialog ? <CoursesModeDetails close={closeCourseDetailsDialog} /> :
<>
<h1 className="centered">Courses</h1>
<CoursesModeSearchFilter />
<CoursesModeTable showCourseDetails={openCourseDetailsDialog} />
<button className="float-btn" onClick={openAddCourseDialog}>
<FontAwesomeIcon icon="map-pin" />
Add Course
</button>
</>}
</CoursesModeProvider>
);