-
-
Save 1gor/a78e83fa1b3bc3c066b8c2c4c062ae04 to your computer and use it in GitHub Desktop.
Compositional Components with Clearwater
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'opal' | |
require 'clearwater' | |
require 'profile_page' | |
class Layout | |
include Clearwater::Component | |
def render | |
ProfilePage.new('yoxtb') | |
end | |
end | |
# Mount an app with a Layout component | |
Clearwater::Application.new(component: Layout.new).call |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'clearwater/black_box_node' | |
require 'clearwater/application' | |
require 'bowser/http' | |
Fetch = Struct.new(:url, :content) do | |
# This mixin declares this component to be a black box. | |
# Clearwater will not try to update it. Instead, it will | |
# only notify the component to mount, update or clean up | |
# after itself. | |
# It is the only Clearwater component type to get notified | |
# about these events. | |
include Clearwater::BlackBoxNode | |
attr_reader :app | |
# This method is similar to componentWillMount (the DOM | |
# element is created, but it is not mounted into the DOM | |
# yet). We get the element | |
def mount element | |
# Because this is a black box, we either have to bring | |
# our own rendering or, in this case, we use another | |
# Clearwater app to take care of rendering for us. | |
@app = Clearwater::Application.new(component: content, element: element) | |
app.render | |
fetch | |
end | |
# This method is similar to React's componentDidReceiveProps, I think. | |
# It's not quite analogous because it's a whole new instance. | |
def update previous, element | |
fetch unless url == previous.url # Only get new data if the URL changed | |
# This is the tricky part. Since we get a new instance on every render, | |
# we copy over what we need from the previous instance. Not everything | |
# will need to be copied, usually just what was setup in the mount method. | |
@app = previous.app | |
app.render | |
end | |
# This is our last lifecycle hook, similar to React's componentWillUnmount. | |
def unmount element | |
end | |
def fetch | |
# We don't have setState, but the idea is that whatever component gets | |
# passed to this one will have setters for loading, data, and error. | |
content.loading = true | |
app.render | |
Bowser::HTTP.fetch(url) | |
.then(&:json) | |
.then do |data| | |
content.data = data | |
content.loading = false | |
app.render | |
end | |
.fail do |error| | |
content.error = 'Could not fetch remote data' | |
content.data = nil | |
content.loading = false | |
app.render | |
end | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'clearwater/component' | |
require 'fetch' | |
require 'profile_view' | |
ProfilePage = Struct.new(:id) do | |
include Clearwater::Component | |
def render | |
Fetch.new( | |
"https://api.myjson.com/bins/#{id}", | |
ProfileView.new, | |
) | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ProfileView | |
include Clearwater::Component | |
attr_accessor :data, :error, :loading | |
def render | |
div({ class: 'some-view', dataset: { loading: loading } }, [ | |
error && p({ style: { color: :red } }, error), | |
data && div({ class: 'profile' }, [ | |
h1(data[:name]), | |
p(data[:bio]), | |
a({ href: data[:url], target: :_blank }, data[:urlName]), | |
]), | |
]) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment