Last active
June 7, 2024 11:25
-
-
Save pabletecodes/899282005ce2408249f3aaa4a7b35ced to your computer and use it in GitHub Desktop.
Design Challenge 002: Simplifying State Management in React
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Design Challenge 002: Simplifying State Management in React | |
// https://juntao.substack.com/p/design-challenge-002-simplifying | |
const ApprovalPanel = ({id}) => { | |
const [isDone, setDone] = useState(false); | |
const handleApprove = () => { | |
fetch('POST', `/rest/approval/${id}/approve`) | |
.then(r => r.json()) | |
.then(data => setDone(data.isDone)); | |
} | |
// handleDecline... | |
// Only show the component when the approval isn't completed | |
if (isDone) { | |
return null; | |
} | |
return ( | |
<div> | |
<h2>This request requires your approval</h2> | |
<Button onClick={handleApprove}>Approve</Button> | |
<Button onClick={handleDecline}>Decline</Button> | |
</div> | |
); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Design Challenge 002: Simplifying State Management in React | |
// https://juntao.substack.com/p/design-challenge-002-simplifying | |
// New actions can be easily added via the actions prop (more flexible and | |
// extensible π). | |
const ApprovalPanel = ({ resourceId, actions }) => { | |
const { isDone, handleWorkflowAction } = useWorkflowActions(resourceId, actions); | |
// Only show the component when the approval isn't completed | |
if (isDone) { | |
return null; | |
} | |
return ( | |
<div> | |
<h2>This request requires your approval</h2> | |
{actions.map((action) => ( | |
<Button onClick={handleWorkflowAction(action.id)}>{action.label}</Button> | |
))} | |
</div> | |
); | |
}; | |
// This hook handles async workflow actions. | |
const useWorkflowActions = ({ resourceId, actions }) => { | |
const [isDone, setDone] = useState(false); | |
const handleWorkflowAction = (actionId) => { | |
const action = findActionById(actions, actionId); | |
if (!action) { | |
throw new Error(`Action '${actionId}' is not supported`); | |
} | |
// This could be extracted to some sort of repository or DAO π | |
const endpoint = buildWorkflowActionEndpoint(resourceId, action); | |
fetch("POST", endpoint) | |
.then((r) => r.json()) | |
.then((data) => setDone(data.isDone)); | |
}; | |
return { | |
isDone, | |
handleWorkflowAction | |
}; | |
}; | |
function findActionById(actions, actionId) { | |
return actions.find(action => action.id === actionId); | |
} | |
function buildWorkflowActionEndpoint(resourceId, action) { | |
return `/rest/approval/${resourceId}/${action.path}`; | |
} | |
// Actions could be made configurable with a simple structure. | |
// I don't like the fact that we're mixing presentation (the label) and data | |
// access concerns (the path) here though π₯ | |
const actions = [ | |
{ id: APPROVE_ACTION, label: "Approve", path: '/approve' }, | |
{ id: REJECT_ACTION, label: "Decline", path: '/reject' }, | |
{ id: NEW_WORKFLOW_ACTION, label: "New action", path: '/new-action' } | |
]; | |
// Sample usage of the ApprovalPanel π | |
const ResourceView = ({ id: resourceId }) => { | |
return ( | |
<div> | |
<ApprovalPanel resourceId={resourceId} actions={actions} /> | |
</div> | |
); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment