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

Idea: React-style state model view rendering #63

Open
jamonholmgren opened this issue Mar 25, 2015 · 11 comments
Open

Idea: React-style state model view rendering #63

jamonholmgren opened this issue Mar 25, 2015 · 11 comments

Comments

@jamonholmgren
Copy link
Member

Per my article posted this morning, I'd like to discuss bringing in a React-style set_state method on PM::Screen and providing a render (or layout) method in the RMQ stylesheet.

Other things to discuss:

  • View hierarchy diffing -- is it worthwhile from a performance standpoint? Updating UI elements in RMQ is super fast, so it might not make sense.
  • Components -- should we have some analog to React.js components?
  • Table screens / collection screens -- this should be easy(ish) with update_table_data and the like, but we should discuss.
  • Animations -- animating between state changes

Lastly, I have to thanks @twerth for building RMQ -- makes this sort of thing so easy. :-)

@jamonholmgren
Copy link
Member Author

An example of a table screen set_state method could be:

def on_load
  set_state({ cells: [] })
end

def set_state(state)
  @cells = state[:cells]
  update_table_data
end

def table_data
  [{
    cells: @cells
  }]
end

@jamonholmgren
Copy link
Member Author

I like the idea of a render method in the stylesheet that smartly binds to state. Thinking out loud:

class MyStylesheet < ApplicationStylesheet
  def render
    append(UIView, :wrapper) do |wrap|
      wrap.append(UILabel, :first_name).data_bind("user.first_name")
      wrap.append(UILabel, :last_name).data_bind("user.last_name")
    end
  end
end

# In screen
def on_something_tapped
  set_state({ user: { first_name: "Jamon", last_name: "Holmgren" } })
end

set_state knows which elements have data bindings. If their data binding values have changed in the new state, it updates them. Think something like this:

# In the screen
def set_state(new_state)
  stylesheet.set_state(new_state)
end

# Stylesheet
def set_state(new_state)
  render unless @rendered
  @rendered = true
  data_bindings.each do |key, element|
    # Extract the value from the hash path,
    # e.g. "user.first_name" is new_state[:user][:first_name]
    new_value = extract_value_from_hash_path(new_state, key)
    # Update only if necessary
    element.data(new_value) if new_value && new_value != element.data
  end
end

The render is only called once, but the bind_data method keeps track of data bindings for future updates. Then those updates are applied on every subsequent set_state call.

@jamonholmgren
Copy link
Member Author

Representing collections of items is another discussion, though. It's probably better to use the new find_or_append method and run render every time.

@jamonholmgren
Copy link
Member Author

Another idea I'm kicking around:

class MyStylesheet < ApplicationStylesheet
  def render
    ui_view(:wrapper) do
      ui_label(:first_name).data_bind("user.first_name")
      ui_label(:last_name).data_bind("user.last_name")
    end
  end
end

This is a more radical departure, though. Perhaps as an RMQ addon.

@willrax
Copy link
Contributor

willrax commented Mar 27, 2015

I wrote this on the community site:

Great article @jamonholmgren. Do you think iterating through the entire UI would cause performance issues down the road?

If this is integrated in to red potion it would be awesome to see reacts state diffing and selected UI updating happening. Really loving this pattern.

@jamonholmgren
Copy link
Member Author

@willrax I think it's pretty rare that we'd have that many UI elements on an iOS screen. If so, however, I do have some ideas (see above) on how we'd accomplish the diffing/selective updates if it ended up being a performance problem.

@jamonholmgren
Copy link
Member Author

Another idea is to do it somewhat manually, but this would be pretty easy to implement:

class MyStylesheet < ApplicationStylesheet
  def render
    append(UIView, :wrapper) do |wrap|
      wrap.append(UILabel, :first_name)
      wrap.append(UILabel, :last_name)
    end
    bind(:first_name, "user.first_name") # state[:user][:first_name]
    bind(:last_name,  "user.last_name")  # state[:user][:last_name]
  end
end

@jamonholmgren
Copy link
Member Author

Closing due to lack of interest.

@andrewhavens
Copy link
Collaborator

Hey @jamonholmgren, I'm still really interested in this. Seems like this could start out relatively simple. Do you have any more thoughts on this after spending more time working with React Native?

@andrewhavens andrewhavens reopened this Oct 10, 2016
@jamonholmgren
Copy link
Member Author

jamonholmgren commented Oct 11, 2016

@andrewhavens Sure! So, two things come immediately to mind. We need a standard way to implement a render function, and we need a set_state function. Something like this comes to mind:

def render(state, root)
  root.append(:container) do |v|
    v.append(:other_view).data(state[:other_view_text])
    v.append(:label).data(state[:label_text])
    if state[:some_conditional]
      v.append(:something).data(state[:something])
    end
  end
end 

One downside is that since this is imperative coding, not "describing a UI tree", it would be harder to do a diff and only update what we want to update when we call set_state. Instead, we're tearing it down and rebuilding it every time. Maybe that's okay, though! At least for now.

set_state can be simple:

set_state({
  something: true,
  items: [ "one", "two", "three" ]
})

It would do a shallow merge, like React does. Deep merge for React sounds good until you actually try to implement it and then you want to quit programming altogether. :)

@jamonholmgren
Copy link
Member Author

Honestly doesn't feel like it would be that difficult to do this, now that I think about it.

@andrewhavens andrewhavens changed the title React-style state model view rendering Idea: React-style state model view rendering Nov 11, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants