-
Notifications
You must be signed in to change notification settings - Fork 479
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
Move responsive sizes to the code-studio Redux store #18861
Conversation
}; | ||
|
||
export function getResponsiveBreakpoint() { | ||
const width = window.innerWidth; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would let caller provide width rather than introduce a dependency on window here.
apps/src/code-studio/responsive.js
Outdated
const store = getStore(); | ||
|
||
window.addEventListener('resize', debounce(() => { | ||
store.dispatch(setResponsiveSize()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be providing a param, right? Should we actually be providing the window width vs. the responsiveSize breakpoint? (possible im misunderstanding something)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, right, I see what you're saying. I'll move the logic out of the reducer and into this file.
For some reason I thought I should provide initial state via getResponsiveBreakpoint
and moved the function definition to the reducer. Instead, I think response.js
calls setResponsiveSize()
immediately with the initial size category (lg/md/sm/xs) in addition to setting up the event listener. Does that sound right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I was maybe thinking about this all wrong. Let me start fresh and state some assumptions about what we're trying to accomplish to see if they are right:
- Goal: We want to be able to easily tell how big the current window is, but bucketed into a few different categories - i.e. we want to know if it is sx, sm, md, or lg.
If true, this is what I would do:
- Provide a setWindowSizeAction
- Have an eventListener that dispatches
setWindowSizeAction
every time we resize (possibly/probably debounced). - Have a reducer that listens for the setWindowSize action, and returns the breakpoint (xs, sm, etc.) for that size.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense! Any reason to have the conversion of width in px to lg/md/sm/xs live in the reducer vs. in responsive.js
?
I guess what I'm asking is whether setWindowSizeAction
should take a px width of the exact window size or one of lg/md/sm/xs. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, we could store the raw window size, and provide a helper so that instead of something like
connect(state => {
breakpoint: state.responsiveBreakpoint
})
we would do
connect(state => {
breakpoint: responsiveBreakpoint(state.windowWidth)
}
Just storing the breakpoint is maybe easier to consume.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think either way works (passing in breakpoint or size)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having the conversion happen in the reducer perhaps make it easier to test.
apps/src/code-studio/responsive.js
Outdated
|
||
/** | ||
* Listen for page resize and dispatch events to Redux when if cross a | ||
* responsive breakpoint width. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that layouts don't only re-render when we cross a breakpoint width. Many of them resize to 100% of current width, so they need to re-render whenever the window changes size (though debouncing helps reduce intensity).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this happen with CSS instead of absolute sizing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Likewise with the course cards I'm testing this out with: can these have float: left;
and let CSS sort out when to reflow them to the next line, based on the available width?
Maybe I'm not understanding how the React components are laid out on the page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As we discussed elsewhere, I retract my initial statement for now. We do have pages whose layout changes in various ways between thresholds, but all of that is CSS rules being applied (often width: 100%
in particular) rather than DOM changes occurring. So, as you've pursued, it does make sense as an optimisation to only force the re-render (through changing state or otherwise) when we cross a threshold. That said, we've survived with a debounced re-render of the DOM upon all resizes in /learn for the past year without known complaint.
As you pointed out, if we did start writing code that depended on values like current window width, then we'd want to re-render the DOM more often than just when crossing the threshold. But afaik, we haven't done that yet.
Ok, I think this is ready for a first pass @islemaster @caleybrock. Thanks! |
My plan is to update the remaining consumers of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's lovely!
apps/src/code-studio/responsive.js
Outdated
|
||
window.addEventListener('resize', debounce(() => { | ||
store.dispatch(setResponsiveSize(getResponsiveBreakpoint(window.innerWidth))); | ||
})); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two suggestions here:
-
Using the default
wait
value of 0 (only waiting until the next tick) may not improve performance enough for lower-end machines. I'd suggest a value of at least 50ms (<=20FPS) - especially not knowing how much behavior we'll attach to this event down the road. -
I might use
_.throttle()
instead of debounce here. With throttle, a very smooth window resize would still trigger intermediate re-renders, albeit at whatever reduced rate we choose.
export function getResponsiveBreakpoint(width) { | ||
return Breakpoints.find(({breakpoint}) => { | ||
if (width > breakpoint) { | ||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Collapse the if (condition) return true
pattern:
return Breakpoints
.find(({breakpoint}) => width > breakpoint)
.responsiveSize;
This is great, I'm excited to have something consistent to use. 👍 |
export const ResponsiveSize = makeEnum('lg', 'md', 'sm', 'xs'); | ||
|
||
// Default window widths that are the starting points for each width category. | ||
const Breakpoints = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Has any further thought gone into these widths? I remember @breville choosing ones that worked well for the page he worked on. 992 matches bootstrap, but I'm not sure where the other ones come from: https://v4-alpha.getbootstrap.com/layout/overview/#responsive-breakpoints
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since our page size width is 970 I'd expect one of the breakpoints to be 972 or something?
Does this mean we can replace/delete either of these: https://github.com/code-dot-org/code-dot-org/blob/staging/apps/src/responsive.jsx It'd be great to not have 3 versions of this responsive work in our code base. There is already confusion about what to use because PLC tools uses bootstrap to make some of their react responsive. |
@caleybrock yep! My plan is to do that in a separate PR, see #18861 (comment). But I can combine everything into one PR if you prefer. |
It's fine to separate if it's a quick followup. 👍 |
Sorry for delay on this. Two questions:
|
No description provided.