Fork me on GitHub
#reagent
<
2018-03-25
>
pbw09:03:53

Can I have component that produces a heading tag, where the level of the heading is derived dynamically? So, for example, I’m passing an arg to the component that contains the heading keyword.

pbw09:03:58

I take it back. Typo in my code.

smogg13:03:10

Any1 has some general tips/examples for how to implement the flip-animation in Reagent (https://medium.com/developers-writing/animating-the-unanimatable-1346a5aab3cd)? I've been bumping my head against the wall for a while now and I can't wrap my head around this

justinlee17:03:37

@smogg why not just use the JS implementation? You can import it and use it just fine.

justinlee17:03:10

I use react-flip-move so I can confirm that works great with reagent.

smogg20:03:10

@lee.justin.m react-flip-move is great, but I needed a more custom behaviour. Plus I'm interested in how that translates into Reagent world. I failed with lifecycle methods tho.

justinlee20:03:01

@smogg it would be a very interesting project particularly if you open sourced it. what specifically were you having trouble with?

justinlee20:03:34

(the project I’d like to see translated is react-virtualized but that’s so freaking complicated)

smogg20:03:26

The main problem was recreating the behaviour of looping over children while in in :component-did-update. I tried using r/children here and doing some magic but ended up with a lot of mess. An easier thing was to use :ref on inner component and save the boundingClientRect() there to some external state (typing example here so may be wrong, but the concept stays the same):

(def state (r/atom {}))


(for [{:keys [id name]} items]
  [:div {:style (get-in @state [:items-tyles id])
            :ref (fn [node]
                    (let [new-bounding-box (.getBoundingClientRect node)
                           old-bounding-box (get-in @state [:bounding-boxes id])
                           delta ....]
                       ;; At this point I have everything I need to calculate the delta
                       ;; so I'd do it, create styles, save the styles in the atom, use the generate styles above
                       (when-not (zero? delta)
                         (swap! state assoc-in [:item-styles id] {:transform (str "translateY(" delta  "px)")}))
                       
                       (swap! state assoc-in [:bounding-boxes id] new-bounding-box)

smogg20:03:06

So basically I tried to move the logic to the :ref part, which is probably wrong (and ended in an endless loop)

smogg20:03:59

if I wanted to actually use :component-will-receive-props and :component-did-update then I don't even know how to get the children like in the React.js example in the article. Maybe I should r/reactify-component at some point to make the r/children fn actually return what I want

smogg20:03:13

It might be a messy explanation but it's late 🙂

justinlee20:03:56

let me take a look at the react example. that should be solvable

justinlee20:03:37

hm so there’s reagent.core/current-component and reagent.core/children, which I think return actual react children. I also think in form-3 components, the lifecycle methods get passed a “this” argument as the first argument and that, I think, is the same thing that reagent.core/current-component returns, but I’m not sure and don’t have the ability to confirm right now

justinlee20:03:34

I don’t think you need to call reactify-component

justinlee20:03:43

putting the logic in the :ref seems like a really bad idea 🙂 I would be inclined to assoc the children to a shared atom if you were going to do it that way

smogg20:03:59

So I think all r/children does is it returns the arguments that you passed to the component. If I had a component:

(defn comp [args & children] ...)
and I passed a vector to it: [:a :b :c] then I'd just get that vector back, not actuall dom nodes with .getBoundingClientRect method

smogg20:03:13

but I'll confirm in a second instead of mumbling

justinlee20:03:21

oh that’s lame. also the docs are wrong if that’s true

smogg20:03:37

I may be wrong.

smogg20:03:17

But the difference is that Reagent component doesn't necessairly follow the [_ args & children] pattern, unlike a React one

smogg20:03:30

Lemme try all that and get back to you

justinlee20:03:45

If you look at https://reagent-project.github.io/news/any-arguments.html it says Note: r/props and r/children correspond to React's this.props and this.props.children, respectively. They may be convenient to use when wrapping native React components, since they follow the same conventions when interpreting the arguments given. which suggested to me that you’d get the actual mounted react components, not just hiccup. that’s why i included that section in the react interop document

justinlee20:03:41

if that doesn’t work, I think it should be able to just use javascript interop here: just grab the react component and access (.-children (.-props comp))

smogg20:03:08

Well, yes - those methods would work with native react.js components. But to me it also means they might behave differently for Reagent component for differences I just mentioned

smogg20:03:45

so:

(defn list-of-things*
  [things]
  [:div
   (for [thing things]
     [:div {:key thing} thing])])

(def list-of-things
  (with-meta list-of-things*
    {:component-did-mount
     (fn [this]
       (println "Children::" (r/children this)))}))

smogg20:03:56

when invoked:

[list-of-things [:a :b :c]])

smogg20:03:04

prints: Children:: [[:a :b :c]]

justinlee20:03:24

huh okay that’s interesting/totally confusing

smogg20:03:08

Yeah, I understood the docs as "this is a method for native react.js components" and looking a the source, this is what's happening: https://github.com/reagent-project/reagent/blob/master/src/reagent/impl/component.cljs#L49

smogg20:03:42

So I thought I Could reactify component so that when I actually use r/children it returns DOM thingies

justinlee20:03:22

probably less disruptive to just get the react component and then access its children

smogg20:03:54

Maybe. But even if I do get the children, then how do I do the rest:

componentDidUpdate(previousProps) {
  previousProps.children.forEach( child => {
    let domNode = ReactDOM.findDOMNode( this.refs[child.key] );

    const newBox = domNode.getBoundingClientRect();
    const oldBox = this.state[key];
    
    const deltaX = oldBox.left - newBox.left; 
    const deltaY =   - ;

    requestAnimationFrame( () => {
      // Before the DOM paints, Invert it to its old position
      domNode.style.transform = `translate(${deltaX}px, ${deltaY}px)`;
      // Ensure it inverts it immediately
      domNode.style.transition = 'transform 0s';  
    });
  });
}

justinlee20:03:03

i don’t quite udnerstand that implementation. is it taking one branch for reagent components and another for react

smogg20:03:08

the domNode.style.transform would indicate using set! in cljs which seems totally wrong

justinlee20:03:18

oops sorry i need to step away. keep me updated

smogg20:03:20

so another way would be to update some external state that holds styles and use those in the render method

smogg20:03:30

no worries, thanks for your input!

justinlee22:03:46

I find that when I’m writing cljs that is very close to something that is has inherently time-based mutations, you just need to write imperative code. otherwise you tie yourself in knots and end up with something that reads very unintuitively. i’m doing this kind of thing right now with a library that draws to a canvas element. there’s no obvious way to write elegant functional code