Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor loading and error #292

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open

Conversation

cbarber
Copy link
Collaborator

@cbarber cbarber commented Aug 4, 2021

No description provided.

React propType failures are signaled via console.error. This prevents
propType failures by overriding the console.error behavior to throw an
erorr instead of logging to STDOUT
@cbarber cbarber force-pushed the cmb/refactor-loading-and-error branch from 239b1b6 to 3582f50 Compare August 4, 2021 21:58
Copy link

@bencates bencates left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not a fan of adding react-recompose as a dependency here, especially given that this codebase serves as a sandbox for the interns to learn in. I don't feel like it adds much, but it does a lot to obfuscate what the HOCs are doing and how they're being applied. The more of the codebase we're able to do without extra dependencies, the smaller the number of concepts and amount of documentation a reader will need to load into their brain to understand what they're looking at.

app/javascript/components/bathroom.jsx Show resolved Hide resolved
Comment on lines +48 to +49
renderWhileError(ErrorMessage),
renderWhileLoading(LoadingMessage),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do these need to take ErrorMessage and LoadingMessage? Those placeholder components are consistent throughout the application, so I think it'd be cleaner to bake them into the respective HOCs.

Copy link
Collaborator Author

@cbarber cbarber Aug 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point. My first grep of the usages of if (loading) / if (error) showed some alternative code paths, but those ended up being FaCC's that would be better solved by bubbling up their graphql fragments. I think defaulting the component seems reasonable.

Comment on lines +47 to +51
export default compose(
renderWhileError(ErrorMessage),
renderWhileLoading(LoadingMessage),
withFragment({ location: getBathroomCode }),
)(Bathroom);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of using the compose function here. It's not saving us much in the way of complexity or line count, but it is an extra third-party dependency and an extra concept the reader will have to understand.

Suggested change
export default compose(
renderWhileError(ErrorMessage),
renderWhileLoading(LoadingMessage),
withFragment({ location: getBathroomCode }),
)(Bathroom);
const BathroomWithError = renderWhileError(Bathroom, ErrorMessage);
const BathroomWithErrorAndLoading = renderWhileLoading(BathroomWithError, LoadingMessage);
const BathroomWithErrorLoadingAndFragment = withFragment(BathroomWithErrorAndLoading, { location: getBathroomCode });
export default BathroomWithErrorLoadingAndFragment

Copy link
Collaborator Author

@cbarber cbarber Aug 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree. It saves us from extra churn when we want to inject/reorder HoCs e.g. if in your example we switch to loading preceding error I should rename three variables.

Comment on lines +9 to +15
export const renderWhileError = errorComponent =>
compose(
setPropTypes({
error: PropTypes.bool.isRequired,
}),
branch(({ error }) => !!error, renderComponent(errorComponent)),
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, I think react-compose obscures more than it reveals here

Suggested change
export const renderWhileError = errorComponent =>
compose(
setPropTypes({
error: PropTypes.bool.isRequired,
}),
branch(({ error }) => !!error, renderComponent(errorComponent)),
);
export const renderWhileError = Component, ErrorComponent => {
const ComponentWithError = (error, ...props) => (
error
? <ErrorComponent />
: <Component {...props} )/>
);
ComponentWithError.propTypes({
...Component.propTypes,
error: PropTypes.bool.isRequired,
}
return ComponentWithError;
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repeating a comment from above, I think it would be cleaner to import { ErrorComponent } here instead of requiring it to be passed in.

@cbarber
Copy link
Collaborator Author

cbarber commented Aug 5, 2021

I am not a fan of adding react-recompose as a dependency here, especially given that this codebase serves as a sandbox for the interns to learn in. I don't feel like it adds much, but it does a lot to obfuscate what the HOCs are doing and how they're being applied.

I agree that there isn't much pay off here for the simple loading/error HoCs. Our pay off is likely further along when we start refactoring lifecycle/state functionality.

I'm picking recompose over roll your own here since it provides conceptual building blocks that describe what is happening. We don't have the expectation that beginners will think too hard about JSX and what it is doing under the hood, just use it as a pattern to generate DOM. I think the compose into a sequence of HoCs reads as a description of behavior that you don't need to think too deeply about to start using.

The more of the codebase we're able to do without extra dependencies, the smaller the number of concepts and amount of documentation a reader will need to load into their brain to understand what they're looking at.

I can get behind the fewer deps and less to learn. I was surprised to see we have a 6MB package when I splitting webpacker/webpack.

I'm going to mark this as DoNotMerge for now until we have some time to mull this over.

@bencates
Copy link

bencates commented Aug 5, 2021

We don't have the expectation that beginners will think too hard about JSX and what it is doing under the hood, just use it as a pattern to generate DOM. I think the compose into a sequence of HoCs reads as a description of behavior that you don't need to think too deeply about to start using.

I couldn't disagree more. The ability to remain ignorant of what's happening under the hood is a privilege! One that can only be earned through often painful experience with the conceptual model.

Beginners, in my experience, are deeply curious about what's happening under the hood and rightly hesitant to lean on abstractions they don't really understand yet. I'd even go so far as to say that an instinct for when abstractions can be trusted is one of the main dividing lines between beginner and intermediate.

@cbarber
Copy link
Collaborator Author

cbarber commented Aug 5, 2021

The ability to remain ignorant of what's happening under the hood is a privilege! One that can only be earned through often painful experience with the conceptual model.

I agree that its a privilege, but feel its one we strive to provide for all of the engineers that will maintain our software. Its not something we gatekeep behind unraveling every technology we lean on to develop modern software. That's a journey we're all still taking.

Beginners, in my experience, are deeply curious about what's happening under the hood and rightly hesitant to lean on abstractions they don't really understand yet. I'd even go so far as to say that an instinct for when abstractions can be trusted is one of the main dividing lines between beginner and intermediate.

I don't think using abstractions and curiosity of how the abstractions work are mutually exclusive. I feel like the abstractions we depend on naturally lead to why questions. Those why questions are the rabbit hole we take to further our understanding.

We should take this meta conversation to the mentor group to further refine our abstraction demarcation point.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants