Giter Site home page Giter Site logo

Comments (3)

back2dos avatar back2dos commented on August 16, 2024 1

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:

  1. 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)
  2. 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.

T1mL3arn avatar T1mL3arn commented on August 16, 2024

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.

back2dos avatar back2dos commented on August 16, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.