Comments (3)
Well, this is not really unexpected. Even in react, if you mount a functional component directly, it will not update when external state it depends on changes. It only rerenders if:
- it has internal state defined via hooks (but then it's not really a functional component anymore - in the sense that it is no longer pure)
- it is rendered by a stateful component, which rerenders as the result of a state change and propagates the new data down.
If you just go ReactDOM.render(<FunctionComponent count={something} />, container))
the component is not going to rerender if something
changes, unless for example something
is backed by mobx and you made FunctionComponent
an observer, in which case it is no longer a pure functional component.
Coconut does not even have functional components per se (at least for now). Functions are simply called and the returned vdom is processed as if you had written it inline.
However, you can put arbitrary subscription/update cycles into any part of the vdom with coconut.ui.Isolated
, if you want.
Renderer.mount(js.Browser.document.getElementById('function'), <Isolated><FunctionView count={count} /><Isolated>);
It's primarily meant to be used within big views to avoid updating the whole DOM when a small part changes, as shown in this simplistic example:
class Counter extends View {
@:state var count = 0;
var rendered = 0;
function render()
return <button onclick={count++}>(rendered {rendered++} times) <Isolated>counter: {count}</Isolated></button>;
}
If you try that, you'll see that the Counter
view itself never needs to rerender - although when removing the Isolated
, the change affects the whole view.
Personally, I would advise against using anything like react portals. So far I've failed to find a use case that isn't better covered by other methods. The example given in the doc are modals, which I would propose implementing via implicits. Among other things that'll allow non-global management of modals (imagine your application has its visual area split in two and each should be allowed to its own modal) and makes testing easier to (the portals based implementation relies on document.getElementById('modal-root');
actually returning something).
Here's a contrived implementation to illustrate the general approach:
import coconut.ui.*;
import coconut.Ui.hxx;
import js.Browser.*;
function main() {
Renderer.mount(
document.body,
<div style={{ height: '100%', width: '100%', textAlign: 'center' }}>
<div style={{ height: '40%'}}>
<Modals>
<Opener />
</Modals>
</div>
<Opener />
<div style={{ height: '40%'}}>
<Modals>
<Opener />
</Modals>
</div>
</div>
);
}
class Opener extends View {
@:implicit var modals:Modals;
function render()
return <button onclick={modals.open(<Modal onclose={modals.close()} />)}>Open</button>;
}
class Modal extends View {
@:attribute function onclose() {}
function render()
return <div style={{ background: 'white', width: '50%', height: '50%', position: 'absolute', left: '50%', top: '50%', transform: 'translate(-50%, -50%)', padding: '20px', boxSizing: 'borderBox', display: 'flex', flexDirection: 'column', justifyContent: 'space-between', alignItems: 'center' }}>
Look at me, I am a modal!
<button onclick=${onclose}>Close</button>
</div>
;
}
@:default(Modals.DEFAULT)
class Modals extends View {
static var counter = 0;
public final id = counter++;
@:children var content:Children = null;
@:state var current:haxe.ds.Option<RenderResult> = None;
public function open(v)
current = Some(v);
public function close()
current = None;
function render()
return <div style={{ width: '100%', height: '100%', position: 'relative' }}>
<Implicit defaults={[Modals => this]}>{...content}</Implicit>
<switch {current}>
<case {Some(v)}>
<div style={{ position: 'absolute', top: '0', left: '0', right: '0', bottom: '0', background: 'rgba(0,0,0,.25)', pointerEvents: 'auto' }}>{v}</div>
<case {_}>
</switch>
</div>
;
static public var DEFAULT(get, null):Modals;
static function get_DEFAULT()
return switch DEFAULT {
case null:
var container = document.createDivElement();
container.style.cssText = 'position: fixed; top: 0; left: 0; right: 0; bottom: 0; pointer-events: none';
document.body.appendChild(container);
Renderer.mount(container, <Modals ref={v -> DEFAULT = v}/>);
DEFAULT;
case v: v;
}
}
from coconut.vdom.
Thanks for such detailed explanation and modal example. Though it is not quite for my case.
Sometimes you have to escape your views layout to avoid styling problem. For example, I am making a userscript that adds views to different places in existing markup (those views include modals as well as other elements). I see no way to do this otherwise than
// <App /> viewDidMount():
Renderer.mount(targetA, <OpenModal />);
Renderer.mount(targetB, <Modal show={state.showModal} />);
Renderer.mount(targetC, <AnotherThing />);
(then I found this issue subject (when my views was functions they did not work) but it appeared to be designed that way and I can fix it anyway using class views)
I could try to patch site's styles to fit my needs, but I believe it is not a good solution (and might be cumbersome), so I tried to craft portal analogue which allows to do things as simple as this:
// <App /> render():
return (
<div>
<AdminPanel />
<if {state.showDialog== true}>
<Portal target={document.body}>
<Dialog text={state.dialogText} />
</Portal>
</if>
<Portal target={somewhereInFooter}>
<Foo value={state.fooValue}>
<Bar value={state.barValue} />
</Foo>
<Bazz />
</Portal>
</div>
);
And this mostly works for simple static layout inside portals (and sometimes for dynamic layout). Though in some cases when such portals get unmounted the app crashes (but this is a subject for separate issue-question).
from coconut.vdom.
Sometimes you have to escape your views layout to avoid styling problem.
In the example I gave the Opener
that's not nested in Modals
makes its modal render completely outside its own view hierarchy. Nothing about the approach forces you render anything into any other particular thing.
The point is that instead of arbitrarily and directly rendering into other parts of the DOM that may or may not be there and that you hardcode in unrelated parts of the view hierarchy, you operate on an implicitly passed entity (the cleanest implementation would pass around a narrow interface rather than a full blown view), for which you have the guarantee that it'll always be there. Among other things, that's easier to test.
The code that you're looking to write depends on both global DOM (document.body
) and internal structure of other components (somewhereInFooter
). I advise against both ;)
from coconut.vdom.
Related Issues (20)
- [diy] Lifecycle event doesn't run when the element rendered conditionally HOT 2
- Unspecified/null attributes not being set
- SVG support HOT 1
- Null<tink.domspec.TagInfo> has no field domCt HOT 2
- Remove xDOM dependency.
- SVG class is not handled properly
- Try to implement <raw> without additional view.
- Split svg and html node diffing.
- Html.Elt.setStyle seems to cause significant slowdown HOT 1
- Obscure bug about nullable function attribute (?) HOT 1
- Try event delegation. HOT 1
- `xdom.html.Dataset` nowhere to be found HOT 2
- Conditional event handlers not removed on re-render
- "role" attributes are stripped from the final rendering HOT 1
- `<time>` tag: `datetime` attribute is stripped from the final rendering HOT 1
- Raw component is one value behind
- Cannot use CSS custom properties (`--bg-color: red`) with style object HOT 1
- `<raw />` component: `class` attribute should not be rendered when empty
- Compile issue on newer haxe versions
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from coconut.vdom.