-
Notifications
You must be signed in to change notification settings - Fork 21
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
See if we can move key
prop from a view we're wrapping into the element produced by lazyView
#14
Comments
I'd like to tackle this one. Any implementation ideas? |
Need to figure out where exactly in React lifecycle we can plug this in and go from there: |
I think the main issue is we're passing a normal function to lazyView. If we used something like a React component we could pass the elements as children and then it's easier to get the Anyways, it's probably simpler to move to |
The whole point of this function is to skip recreating the children, so
even if something else is easier I don't want to change that specific
aspect.
|
Was trying to find React JS examples where one would refer to a Higher-Order Component children in this way, but no success so far. |
I think there are 2 stages that lazy needs to be aware of and handle differently:
|
Following works when key is present on rendered child: type LazyProps<'model> = {
model:'model
render:unit->ReactElement
equal:'model->'model->bool
key:string // added key prop
}
let getKey (view:'model->ReactElement) (state:'model) : string =
let render = view state
!!render?key
let lazyViewWith (equal:'model->'model->bool)
(view:'model->ReactElement)
(state:'model) =
ofType<LazyView<_>,_,_>
{ render = fun () -> view state
equal = equal
model = state
key = getKey view state }
[]
Downsides are:
|
Don't think it needs to (or even could, in any meaningful way) - functionally nothing should depend on it being there on the lazy element, the child view should be in control.
Even doing it once when we shouldn't would defeat the purpose. I can see the problem though, I think - the key is just an eager value from React perspective and I think it won't let you change it from inside the component either... Need to dig deeper and really think outside the React box. |
What I meant when I said it doesn't work with missing key on child was it sets the How about always setting the |
In general, a This is indeed kind of what we want, but I don't know if React will re-use the existing DOM elements or always destroy all the elements in this case even when patching would be more efficient. |
Alternatively we could add one more parameter to |
This works if we allow to specify [<Emit("delete $0['$1']")>]
let deleteProp x (prop : string) = jsNative
let lazyViewWith (equal:'model->'model->bool)
(hash:'model->int option)
(view:'model->ReactElement)
(state:'model) =
let key =
match hash state with
| Some hashCode -> string hashCode
| None -> null
let props =
{ render = fun () -> view state
equal = equal
model = state
key = key }
if isNull key then deleteProp props "key" // do not add key if `None`
ofType<LazyView<_>,_,_>
props
[]
let lazyView (view:'model->ReactElement) =
lazyViewWith (=) (fun _ -> None) view |
@theimowski I like where this is heading! |
I don't think we can use view hash - given the following: let lazyViewWith (equal:'model->'model->bool)
(view:'model->ReactElement)
(state:'model) =
let key = (view :> obj).GetHashCode() |> string
let props =
{ render = fun () -> view state
equal = equal
model = state
key = key }
ofType<LazyView<_>,_,_>
props
[]
let lazyView (view:'model->ReactElement) =
lazyViewWith (=) view
let menuItem label page currentPage =
lazyView (fun (page, active, label) ->
li
[ ]
[ a
[ classList [ "is-active", active ]
Href (toHash page) ]
[ str label ] ]
) (page, page = currentPage, label)
let menu (model: Model) =
aside
[ ClassName "menu" ]
[ p
[ ClassName "menu-label" ]
[ str "General" ]
ul
[ ClassName "menu-list" ]
[ ofList
(model.Pages
|> List.map (fun (l,p) -> menuItem l p model.CurrentPage )) ] ]
the key of each item changes every time I add a new item to model Basically I don't think we can assume the |
Hmm, I'd never write it like that, I always apply lazy in the
outside/parent view which seems to offer consistent hash.
|
Can you post an example? |
Frankly I don't understand why it gives consistent hash (even if you partially apply it OR across recompiles, according to the REPL), but your way constructs a new function, mine passes a stable reference and it remains stable. Here's how I'd write this: let menuItem (page, active, label) =
li
[ ]
[ a
[ classList [ "is-active", active ]
Href (toHash page) ]
[ str label ] ]
let menu (model: Model) =
aside
[ ClassName "menu" ]
[ p
[ ClassName "menu-label" ]
[ str "General" ]
ul
[ ClassName "menu-list" ]
[ ofList
(model.Pages
|> List.map (fun (l,p) -> lazyView menuItem (p,model.CurrentPage,l))) ] ] |
Even if I do it your way, |
Hmm, that doesn't make sense, even in CLR world that should have been stable, unless that cast is doing something strange. |
@alfonsogarciacaro brings up a good point that
lazyView
wrapped elements don't expose/inherit thekey
prop (not their own, nor from the view they are embedding), resulting in a dicey diff job for React when dealing with lists.I think it would be nice to see if there's
key
prop in the view we're wrapping and move the prop from the embedded element up into the one produced by thelazyView
family of functions.The text was updated successfully, but these errors were encountered: