-
-
Notifications
You must be signed in to change notification settings - Fork 100
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
Controlled vs uncontrolled #52
Conversation
Also cc @ismay and @jeremybini for feedback. |
cf7bd6b
to
6c55676
Compare
It's worth noting that I tried to refactor this completely as part of this PR, even having the zoom image as a "child" component, but then I recalled why it has to be in a "portal" and create a new react node at the end of the |
Have to say I don't feel qualified to review this in depth. Haven't dealt a lot with controlled components other than just form elements. But from what I can tell it looks good. |
This is looking really good! Thanks for your hard work on this. Always fire
|
@cbothner great review. Really. I agree that Do you think anything at all should get passed back? How does this sound? |
Or should it send back both the original click event and that object or both combined into one? |
@cbothner You bring up a good point with those scroll/touch listeners. Should they even be set if it's a controlled component? Following my |
I actually don’t think we should combine And certainly we do need to set the listeners even if it is a controlled component. Scrolling or clicking or whatever on the zoomed image still needs to be a way to unzoom an image (by firing the |
Okay thanks for clarifying that. I've been playing with your test component and am close to some more stuff. |
I need to take a break. I cannot currently find a way to have this work in a props-passing way, as the This struggle 🚌 makes me feel like I need to rethink some things. TBD. |
30ae8c1
to
864073a
Compare
(rebased with master and put current work up here) |
36bae68
to
58f3566
Compare
58f3566
to
5bedd22
Compare
@cbothner you may have issue with what I'm about to describe, so please hear me out: The more I tried to figure something out to address your thoughts above with regards to the component "reporting back" when it should be closing but without doing the actual closing, the less it made sense to me to have the component care about that and be responsible for such actions. If the consumer has chosen to be in control of this thing, then shouldn't it be the one in control? Instead, I dug deeper and identified two different sets of controls that determine what the zoom component should respond to:
I came to this conclusion when I realized that all the conditional logic had to do with events and what should happen when the consumer is in control and what happens when the component controls itself. Here's what it essentially looks like with this system: controlled component <ResizeWrapper>
<Zoom ... />
</ResizeWrapper> uncontrolled component <FullWrapper>
<ResizeWrapper>
<Zoom ... />
</ResizeWrapper>
</FullWrapper> but this latter one includes the Since the only communication we have outside of emitting global events is accessing a top-level component through the "portal" and calling some method on that, I have each one of these wrappers clone their child and add a Here's what I tested with in the example file (first image responds to import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import ImageZoom from '../../lib/react-medium-image-zoom'
class App extends Component {
constructor(props) {
super(props)
this.state = { isZoomed: false }
}
componentDidMount() {
document.addEventListener('keyup', (e) => {
switch (e.keyCode) {
case 74:
return this.setState({ isZoomed: true })
case 75:
return this.setState({ isZoomed: false })
default:
return
}
})
}
render() {
return (
<div className="container">
<h1>Image Zoom</h1>
<p>Trust fund seitan chia, wolf lomo letterpress Bushwick before they sold out. Carles kogi fixie, squid twee Tonx readymade cred typewriter scenester locavore kale chips vegan organic. Meggings pug wolf Shoreditch typewriter skateboard. McSweeney's iPhone chillwave, food truck direct trade disrupt flannel irony tousled Cosby sweater single-origin coffee. Organic disrupt bicycle rights, tattooed messenger bag flannel craft beer fashion axe bitters. Readymade sartorial craft beer, quinoa sustainable butcher Marfa Echo Park Terry Richardson gluten-free flannel retro cred mlkshk banjo. Salvia 90's art party Blue Bottle, PBR&B cardigan 8-bit.</p>
<p>Meggings irony fashion axe, tattooed master cleanse Blue Bottle stumptown bitters authentic flannel freegan paleo letterpress ugh sriracha. Wolf PBR&B art party aesthetic meh cliche. Sartorial before they sold out deep v, aesthetic PBR&B craft beer post-ironic synth keytar pork belly skateboard pour-over. Tonx cray pug Etsy, gastropub ennui wolf ethnic Odd Future viral master cleanse skateboard banjo. Pitchfork scenester cornhole, whatever try-hard ethnic banjo +1 gastropub American Apparel vinyl skateboard Shoreditch seitan. Blue Bottle keffiyeh Austin Helvetica art party. Portland ethnic fixie, beard retro direct trade ugh scenester Tumblr readymade authentic plaid pickled hashtag biodiesel.</p>
<div>
<ImageZoom
image={{
src: 'bridge.jpg',
alt: 'Golden Gate Bridge',
className: 'img'
}}
zoomImage={{
src: 'bridge-big.jpg',
alt: 'Golden Gate Bridge',
className: 'img--zoomed'
}}
isZoomed={this.state.isZoomed}
/>
</div>
<p>Thundercats freegan Truffaut, four loko twee Austin scenester lo-fi seitan High Life paleo quinoa cray. Schlitz butcher ethical Tumblr, pop-up DIY keytar ethnic iPhone PBR sriracha. Tonx direct trade bicycle rights gluten-free flexitarian asymmetrical. Whatever drinking vinegar PBR XOXO Bushwick gentrify. Cliche semiotics banjo retro squid Wes Anderson. Fashion axe dreamcatcher you probably haven't heard of them bicycle rights. Tote bag organic four loko ethical selfies gastropub, PBR fingerstache tattooed bicycle rights.</p>
<div style={{ float: 'left', margin: '0 1.5em 0 0' }}>
<ImageZoom
image={{
src: 'nz.jpg',
alt: 'Picture of Mt. Cook in New Zealand',
className: 'img',
style: {
width: '20em'
}
}}
zoomImage={{
src: 'nz-big.jpg',
alt: 'Picture of Mt. Cook in New Zealand',
className: 'img--zoomed'
}}
/>
</div>
<p>Ugh Portland Austin, distillery tattooed typewriter polaroid pug Banksy Neutra keffiyeh. Shoreditch mixtape wolf PBR&B, tote bag dreamcatcher literally bespoke Odd Future selfies 90's master cleanse vegan. Flannel tofu deep v next level pickled, authentic Etsy Shoreditch literally swag photo booth iPhone pug semiotics banjo. Bicycle rights butcher Blue Bottle, actually DIY semiotics Banksy banjo mixtape Austin pork belly post-ironic. American Apparel gastropub hashtag, McSweeney's master cleanse occupy High Life bitters wayfarers next level bicycle rights. Wolf chia Terry Richardson, pop-up plaid kitsch ugh. Butcher +1 Carles, swag selfies Blue Bottle viral.</p>
<p>Keffiyeh food truck organic letterpress leggings iPhone four loko hella pour-over occupy, Wes Anderson cray post-ironic. Neutra retro fixie gastropub +1, High Life semiotics. Vinyl distillery Etsy freegan flexitarian cliche jean shorts, Schlitz wayfarers skateboard tousled irony locavore XOXO meh. Ethnic Wes Anderson McSweeney's messenger bag, mixtape XOXO slow-carb cornhole aesthetic Marfa banjo Thundercats bitters. Raw denim banjo typewriter cray Tumblr, High Life single-origin coffee. 90's Tumblr cred, Terry Richardson occupy raw denim tofu fashion axe photo booth banh mi. Trust fund locavore Helvetica, fashion axe selvage authentic Shoreditch swag selfies stumptown +1.</p>
<div>
<ImageZoom
image={{
src: 'gazelle.jpg',
alt: 'Gazelle Stomping',
title: "Don't exceed original image dimensions...",
className: 'img',
style: {
width: '80%'
}
}}
shouldRespectMaxDimension={ true }
/>
</div>
<p>Scenester chambray slow-carb, trust fund biodiesel ugh bicycle rights cornhole. Gentrify messenger bag Truffaut tousled roof party pork belly leggings, photo booth jean shorts. Austin readymade PBR plaid chambray. Squid Echo Park pour-over, wayfarers forage whatever locavore typewriter artisan deep v four loko. Locavore occupy Neutra Pitchfork McSweeney's, wayfarers fingerstache. Actually asymmetrical drinking vinegar yr brunch biodiesel. Before they sold out sustainable readymade craft beer Portland gastropub squid Austin, roof party Thundercats chambray narwhal Bushwick pug.</p>
<p>Literally typewriter chillwave, bicycle rights Carles flannel wayfarers. Biodiesel farm-to-table actually, locavore keffiyeh hella shabby chic pour-over try-hard Bushwick. Sriracha American Apparel Brooklyn, synth cray stumptown blog Bushwick +1 VHS hashtag. Wolf umami Carles Marfa, 90's food truck Cosby sweater. Fanny pack try-hard keytar pop-up readymade, master cleanse four loko trust fund polaroid salvia. Photo booth kitsch forage chambray, Carles scenester slow-carb lomo cardigan dreamcatcher. Swag asymmetrical leggings, biodiesel Tonx shabby chic ethnic master cleanse freegan.</p>
<p>Raw denim Banksy shabby chic, 8-bit salvia narwhal fashion axe. Ethical Williamsburg four loko, chia kale chips distillery Shoreditch messenger bag swag iPhone Pitchfork. Viral PBR&B single-origin coffee quinoa readymade, ethical chillwave drinking vinegar gluten-free Wes Anderson kitsch Tumblr synth actually bitters. Butcher McSweeney's forage mlkshk kogi fingerstache. Selvage scenester butcher Shoreditch, Carles beard plaid disrupt DIY. Pug readymade selvage retro, Austin salvia vinyl master cleanse flexitarian deep v bicycle rights plaid Terry Richardson mlkshk pour-over. Trust fund try-hard banh mi Brooklyn, 90's Etsy kogi YOLO salvia.</p>
</div>
)
}
}
const container = document.querySelector('[data-app]')
ReactDOM.render(<App />, container) This is what makes the most sense to me, and I'm not quite technically sure how to do it any other way at the moment. If you can spare the time, what are your thoughts on this? Thank you for helping me. |
Hey @rpearce. Thanks so much for the hard work. I'm slammed today, so I'll take a look tomorrow morning. |
Hey. Sorry I'm not better able to help implement this; I feel like I'm asking so much work of you while contributing so little. I see where you're going with this. Certainly To be honest, I'm forming my expectations for how your component should work from palantir/blueprint's Ignoring their On another smaller note about your last commit here: I think that string refs are deprecated, so you might want to consider avoiding them... Anyway, seriously thank you for your commitment to making this library better for all of us. I know how hard it is to find the time around everything else we all have to do, and how much pressure we users can put on you maintainers. I've figured out what I need as workarounds, so please don't take any of my comments as an expectation or pressure. Whatever you can do: it's appreciated. |
@cbothner Not a problem. That's the burden of maintainership! I'll have a look around those links, think some more, push stuff around some more and reach out to some colleagues of mine for ideas. |
@cbothner after speaking with a colleague and hearing a great "that's why people use libraries" analysis, I'll see about combining the functionalities back together and triggering some sort of callback instead of updating internal state. However, I can't say what that API will look like just yet. |
Update on this: I'm still traveling in the USA but will be back in New Zealand starting next week where I'll pick this back up. |
…sumer when it would normally close
50bcdac
to
1e3e9f3
Compare
@cbothner Hello again! I've been granted a lot of time this week to work on this. If you get some time, can you check this out again and see if it is more to your liking?
Other things:
|
I feel pretty good about this after testing in another app. If there are any adjustments to be made that y'all can think of, we can cut a patch release. cc @rajit |
Looking forward to putting eyes on this (and those new issues) tomorrow—today was crazy and I apologize for not getting around to it. From what you’ve described I’m thrilled. One quick thought is that you should not feel bad about having to do a new render instance. In my experience that is actually the right way to do this kind of thing; portals are a yet-undocumented upcoming feature of React 16.
Of course the new implementation is declarative and wickedly optimized... shiny new toys soon! |
@cbothner thanks for those links. Also keep a heads up -> I've got 2 bug fixes coming in hot, as well as implementing a basic React Storybook to show all the different options. |
Works really well for me 👍 |
Fixes #30
Fixes #49
isZoomed
on component instantiation, the component should only zoom/unzoom when thatisZoomed
prop changes. This means that the consumer is in charge of when it's open or closed. Clicking on the image will not do anything. If it is a controlled component but then you do not passisZoomed
with a non-null value, then an error will be raised telling you that you must control it.true
orfalse
toisZoomed
, then the component will control itself. This means that if you initially passisZoomed={null}
and then passisZoomed={true}
(orfalse
), then it will throw an error. If it is an uncontrolled component but then you do passisZoomed
with a non-null value, then an error will be raised telling you that you must not control it.Please help test the sh*t out of this.
cc @cbothner and @rajit