Reagent generate a React Component that passes React.isValidClass(component)? - reactjs

I'm trying to use react-router in my Clojurescript Reagent project. The problem is, react-router requires that components pass React.isValidClass(component), which in React 0.11.2 is defined as:
ReactDescriptor.isValidFactory = function(factory) {
return typeof factory === 'function' &&
factory.prototype instanceof ReactDescriptor;
};
Reagent seems to generate components as an object instead of a function. Here is my code:
(defn home []
[:div [:h1 "Home Page placeholder"]])
(reagent/as-component (home)) ; => #<[object Object]>
Has anyone worked out how to make this sort of interop work?

What reagent-react-router does to make this work is use reagent.core/reactify-component. The reactify-component exists to make Reagent components valid React components for these kinds of inter-op scenarios.

Related

Framer Motion Library "hooks" not working with reagent

The path tag of framer motion can be used in conjunction with useViewportScroll to create a scroll info path.
const { scrollYProgress } = useViewportScroll()
return (
<motion.path style={{ pathLength: scrollYProgress }} />
)
When used with Clojurescript, this doesn't work:
(def div (.-div motion))
(def path (.-path motion))
(defn my-component []
[:> div
[:> path {:style {:pathLength (.-scrollYProgress (useViewportScroll))}}]]
)
The error is:
Invariant Violation: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See for tips about how to debug and fix this problem.
-- EDIT --
Based on an answer, I changed to this:
(defn path []
(let [path (.-path motion)
path-length (.-scrollYProgress (useViewportScroll))]
(r/as-element
(do
(js/console.log "path length is " path-length)
[:> path {:style {:pathLength path-length}}]
))))
and use [:> path] in my-component. But the console log isn't triggered when I scroll the page, suggesting that the path-length variable isn't changing when scroll, i.e., the path component isn't remounted with the scroll. How to fix this?
Hiccup style components won't work with Hooks, because in order to interpret the returned hiccup, Reagent creates a class component, not a functional component (Frankly this is probably a historical oddity as Reagent pre-dates the hooks API).
Instead you need to create a functional component by passing reagent.core/create-element a function, but in that function you either will have to create React DOM elements by hand, or convert the hiccup yourself. See: https://github.com/reagent-project/reagent/blob/master/doc/ReactFeatures.md#function-components

How safely to pass params to react component with react-rails

I'm using react-rails to add some react componenets to an existing ruby on rails app. I just realized that all the props being passed initially to the component are easily seen if you inspect the component
<%= react_component('ProfileWeeklyWriting', {
languages: #user.languages,
currentUser: #current_user,
responses: #writing_responses,
clapLottie: asset_url('lottie/clap.json'),
clapIcon: asset_url('icons/clap.svg'),
arrowIcon: asset_url('icons/arrow_green.png')
}) %>
But when you inspect the element, allll those variables are shown!
I know I can just do an ajax call from within the component, but is there a way to pass variables to the component initially, without them being shown to the world?
Let's take a bit theory about how it works. When you do classic SPA without any backend engine you usually do something like
ReactDOM.render(<App />, document.getElementByID('root'))
Which simply render the root component on place of <div id="root" />. When you apply Rails templates on the top, you are connecting 2 different worlds. ReactDOM and Rails slim templating engine (probably) but they don't know nothing about each other. ReactRails is really simply routine which does something like this:
1 Inject custom react-rails script to page
Wait for DOM ready
Collect all [data-react-class] elements
Iterate through of them and call ReactDOM with props.
You can think of it like calling several "mini apps" but in fact those are only components (react doesn't distinguish app/component, it's always just a component)
So the code is something like this (I didn't check the original code but I wrote own react-rails implementation for my company)
function init () {
const elements = document.querySelectorAll('[data-react-class]')
if (elements.length > 0) {
Array.prototype.forEach.call(elements, node => {
if (node) {
mount(node)
}
})
}
}
function mount(node) {
const { reactClass, reactProps } = node.dataset
const props = JSON.parse(reactProps || '{}')
const child = React.createElement(window[reactClass], props)
ReactDOM.render(child, node)
}
Then the DOM ready
document.addEventListener('DOMContentLoaded', e => {
init()
})
Son in fact Rails doesn't know anything about React, and React doesn't know anything about Rails unless it's not living on window. (THIS METHOD IS HIGHLY DISCOURAGED.
In real world there are ways how to make "server" rendering, which means that this piece of code is done on server to not expose props and whole React lifecycle and just flush real prepared HTML to DOM. That means that in the lifecycle BEFORE HTML is sent to the client, there is called transpiler which compiles those components, you can read about it here
https://github.com/reactjs/react-rails#server-side-rendering
So it just calls those methods with a help of https://github.com/rails/execjs
So the only way how to "not expose" props to the client is to "pre-render" components on backend by some engine (either some JS implementation for your language or directly node.js backend). I hope I gave you a better picture how it works and how it can be solved!

3d.io api at React project

Does 3d.io support React components at future ? Now, I need to find dom element utilized "ref" from component to retrieve io3d objects.
render () {
// this.props.elements is the state from getAframeElements
if (this.props.sceneId !== '') {
this.props.elements.forEach( (item) => {
this.refs.scene.appendChild(item)
})
}
return (
<a-entity ref="scene">
</a-entity>
)
}
Do you have any guides how to use 3d.io at React project ? Or I need to use document.querySelector after componentDidMount event at React.
A few things here:
How to use react and 3d.io
Most 3dio-js methods return plain js objects and arrays that you can use with any javascript library.
Here's how you could use io3d.furniture.search in a react component: https://jsfiddle.net/wo2xpb9g/
How to combine react and a-frame
There is the aframe-react project that will make combining a-frame and react easier https://github.com/ngokevin/aframe-react
As of newer versions of react it seems possible to combine a-frame and react directly like you've done, here's an example: https://codepen.io/cassiecodes/pen/akXWjo?editors=1010
How to use io3d.scene.getAframeElements with react
getAframeElements is a convenience method which converts sceneStructure into real a-frame DOM nodes. In react projects, real DOM nodes are generated by react itself.
There are two possibilities,
Instead of getAframeElements, convert sceneStructure from io3d.scene.getStructure into react virtual DOM yourself. Hopefully a community library will be published for this soon.
Keep the a-frame DOM separate from react. This is the approach sometimes used to combine react with libraries such as D3, which directly manipulate the DOM... that approach is discussed with examples here: https://medium.com/#Elijah_Meeks/interactive-applications-with-react-d3-f76f7b3ebc71

react & i18next, why do i need react-i18next

I am new to i18next and I dont really understand why there is a need to use react-i18next when playing with react.
Using an external library like moment.js in my component i do : const date = moment(this.state.date)
Can I do the something like below with i18next :
import i18next from 'i18next';
const translated = () => (
<h1>{i18next.t('title')}</h1>
)
Thanks
sure you can directly use i18next. react-i18next is only some optimization to use it in react -> eg. rerender on language change, or dynamic load of the namespaces using the hoc or render prop. Or the Trans component which allows you to nest react components into translations. But there is no magic, like there is no magic in jquery-i18next or vue-i18next -> it just simplifies your task.

React js in Aurelia js, call aurelia function

Am very new to react js and aurelia js, my requirement is use react for view and aurelia for model/routing and all. I refered this doc for installation, it works well. Done like this,here my view is in react js, while clicking the details in the table(doted icons),needs to call a function which should be in aurelia.
I followed the above mentioned doc for coding, instead of list in the my-react-element.jsx i changed into table style like in the pic.
Yeah, i got it. just understand passing value to components (custome-component in aurelia, component in react). In both, aurelia and react it works nearly same(like call-back function).
Code :
react-example.js Just declare a function, which you want to execute.
myfun(){
alert('Hi, am in aurelia..');
}
react-example.html use bind to set the declared function to custome-component.
<react-element myfun.bind = "myfun"></react-element>
react-example.js
#bindable('myfun')
<MyReactElement data={this.data} myfun={this.myfun}/>,
my-react-element.jsx
<p onClick = {this.try.bind(this)} >click </p>
try() {
this.props.myfun(); }
Note : Basic knowledge of react and aurelia required and compare the doc to fix.

Resources