Skip to content

Instantly share code, notes, and snippets.

@pabletecodes
Last active June 7, 2024 11:25
Show Gist options
  • Save pabletecodes/899282005ce2408249f3aaa4a7b35ced to your computer and use it in GitHub Desktop.
Save pabletecodes/899282005ce2408249f3aaa4a7b35ced to your computer and use it in GitHub Desktop.
Design Challenge 002: Simplifying State Management in React
// 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>
);
}
// 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