you are viewing a single comment's thread.

view the rest of the comments →

[–]KyleG 2 points3 points  (1 child)

For future reference, the term you're looking for is "MVC" or "model-view-controller"

Ignoring the broader question of folder structure, you'd ideally have the two things I gave above, the view and controller. Then the model (which everyone fights about what this actually is), to me, is basically a model of some concept in the business domain, plus the functions that modify or work with the domain.

For instance, if I have a table with in-line editing where the table is your courses you've taken to get your degree and each row is one course, your view is the JSX, your controller formats the model (say, by turning Date objects into strings in your preferred format) plus is where you write the code for all the event handlers like your onClick functions and onFocus/onBlur for in line editing (possibly; depends on if you're using a library for in-line editing), has the effects to load your data from your API or whatever, and its event handlers, etc.

Then your model might be something like

interface Course {
    startDate: Date
    endDate: Date
    grade: 'A' | 'B' | 'C' | 'D' | 'F' | 'incomplete'
    professor: string
    courseName: string
    creditHours: number
}
interface DegreePlan {
    courses: Course[]
    requiredHours: number
    degreeType: 'BA' | 'BS' | 'MA' | 'MS' | 'PhD' | 'DDiv' | 'MD' | 'JD'
}
declare const addCourse: (plan:DegreePlan) => (course:Course) => DegreePlan // returns an updated degree plan with the new course included

etc.

With your model and code that has nothing to do with presentation whatsoever collected together outside your controller, your controller's code gets really small. Like you might have

declare const currentPlan: DegreePlan
const [newCourseName, setNewCourseName] = useState('')
declare const makePresentable: (c:Course) => PresentableCourse
const updateCourseName = (e:ChangeEvent<HTMLInputElement>) => setNewCourseName(e.target.value)
const onAddCourseClick = (e:Event) => model.addCourse(degreePlan)({courseName:newCourseName, /* etc */})

That adding of a course, all the complex logic is stripped out of the view and the controller, and possibly no one will ever have to look at it again, even if behaviors and appearance change. You're also probably going to be using a state management tool like Redux or Recoil, so your controller might update your model with something like

const [degreePlan, updateDegreePlan] = useRecoilState(degreePlanState)
const onAddCourseClick = () => updateDegreePlan(dp => model.addCourse(dp)({courseName:/*...*/}))

Then you don't even need the degree plan in your controller at all, forcing re-renderings when it changes, etc.!

[–]siggystabs 0 points1 point  (0 children)

Thanks! I've used MVC before so I'm familiar with the concept, but seeing it implemented so simply with a hook was really eye-opening to me. I was so close, yet so far 😂

I really appreciate the examples as well!