-
Notifications
You must be signed in to change notification settings - Fork 35
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
Opal 0.8 - WIP noise reducing syntax changes #32
base: opal-0.8
Are you sure you want to change the base?
Conversation
Hi, I've go through the features you proposed, for
I didn't get it, would you mind elaborate more with some examples of the problem you are faced? |
Sure in react when you update a state, you cannot immediately read the state and be guaranteed to get the value back till after the render. This makes sense in react.js as the syntax is very cumbersome anyway. but in ruby its nice to be able to do this:
The thing is this works sometimes without out any changes but sometimes it doesn't. I got really confused debugging some code, and then I checked the reactjs docs and discovered that setting a state does not guarantee that you can immediately read the state!!! So I just copy the state to a Ruby hash that gets merged with the ReactJs hash. |
FYI... I am working on changing the way the dsl references Opal components: right now if the component is The ReactJS convention seems to be the capitalize component names in JSX so I think So you would write def render
div do
FooBar title: "its a foo bar"
end
end I would also like to be able to write def render
div do
SharedComponents::FooBar title: "its a foo bar"
end
end but this is going to take a little more effort, but I think I can get it to work. |
I think caching state for getter is a great idea, would you mind make a separate request for this feature?
👍 on this, maybe some const missing magic? |
That's exactly where I am heafing |
I tried out using a const name (i.e. FooBar instead of foo_bar) for client defined components. It works nice, unless the component has no block and no parameters (I think that is a real tails case). For such a component to work you need to include at least an empty block so that ruby interprets the expression as a method call and not just a constant. For example if you say I have one more thing to try to get around this, we will see... |
Okay! Got component classes to name space correctly, and things read quite nicely... in addition I setup a full two linkage (based on React Linkages) that allows a single param (prop) to have 2 way communication with the parent. Like React Linkages its just syntax sugar to make things easier to ready. I am going to update my initial comment with the pull request so all the info is one place. |
Just added some more syntax sugar and noise reduction. you can now say
You can also say
the result of the block becomes the default initializer for any states not having their own initial values. So everything works per the current syntax. After reading through the react documents I don't even think its a good idea to have the initializer execute in the context creating a new instance of the component, so I left the code just like it is now. Also made it so any param (whether declared in the params validation or not) gets responds as a method. So you can say Finally I did add (at least to try it out) having Strings respond to a display method. This will just put the string into the render stream. Not sure if its really needed or not but it is allowed by React |
I like the define_state initial value shortcuts and params shortcuts. For reading state, is it really a good idea to cache state or encourage people to read state like this? I'm no expert, but I think React does this for a reason and that is it bunches up state updates so that multiple renders aren't needed. I realize this PR may not be fundamentally changing that, If we start presenting different behavior with this Ruby layer on top, people might become a bit too distant from what's going on underneath. |
@weid03 The intention is to make the reading/writing state syntax simple, without changing the semantics. As far as writing goes, the implementation always writes through to the react hash, so what ever optimizations react has should be in effect. There is one "semantic" difference, that is intentional. React does not guarantee the that if write to a state, you can immediately read the same value. I do guarantee it hence the cache. I don't think this changes anything because it just puts a handy wrapper around writing to state that anybody could do themselves. I think the only reason is does not work this way in react is because in JS you couldn't really make it read nicely anyway. To put it another way: The opal-react "state" is a wrapper around the the react-js state. The react-js state is still there. can you think of a case where something bad might happen using the higher level opal-react state representation? |
Fix a small formatting issue in the readme
@catmando merging the reactive-ruby branch into master closes this entire PR. Correct? |
This is WIP, just to see what you think of the changes.
For now its using my own copy of opal that better source mapping than the current version 0.7...
Summary of changes for discussion:
Any class with a render method can be used in the dsl by using the class name.
Component classes can also be scoped normally, so in the above example
MyComponent
could be a child, sibling, etc ofParentComponent
. So ifParentComponents
full name wasComponents::Admin::ParentComponent
, thenMyComponent
could be a child ofComponents, Components::Admin, or Components::Admin::ParentComponent
.In the case where
MyComponent
was somewhere else in the module hierarchy the name can be qualified as needed. i.e.Shared::MyComponent
params can be specified as one a liner (similar to state, and rails attributes etc.)
params get their own read accessors just like state, so you can say
params with type
Proc
, can be called inside the component without using call.state can now be updated by using the ! methods.
the object returned by some_state! will also respond to :call, this allows
state to be easily passed as a Proc to a child param. For example
params can be of type React::Observable. Params of this type act like a Proc with an initial value. They can be read in the usual way, and can be update by calling the ! version. So they work like react linkages. State variables with the ! on the end are actually Observables, so you can now simply write:
If needed you can create an observable on the fly by calling
watch(initial_value) { | updated_value | ... }
A copy of the current state is kept, so it can be read during events.
ReactJS does not guarantee the value of state being stable until after
the event completes.
So now its safe to write
The React.render method can be passed a Opal Element directly (no need to do a get(0).
There is a React::TopLevelComponent class. This provides several features:
elsewhere in old js code you can say
Any calls to external updates will be queued until the top level component is mounted
The TopLevelComponent class can be defined across multiple files. This means it can be used to control the app layout across multiple pages, with each page adding its own "top level" behavior inside the page.
Well I think that's it... interested in what you think... I am using all these changes for my main company app, and should be deploying soon, so you can see it live in action.
I am happy to write the docs, update the samples, and of course write tests for all these.