Isomorphic hyperHTML components without passing in wires - javascript

I have the following two components:
// component.js
// imports ...
function ListItem(item) {
const html = wire(item)
function render() {
return html`<li>${item.foo}</li>`
}
return render()
}
function List(items) {
const html = wire(items)
function render() {
return html`<ul>${items.map(ListItem)}</ul>`
}
return render()
}
I want to put them in a module which is shared between the client and the server. However, as far as I can tell, although the API pretty much identical, on the server I have to import the functions from the viperHTML module, on the client I have to use the hyperHTML module. Therefore I can not just import the functions at the top of my shared module, but have to pass to my components at the call site.
Doing so my isomorphic component would look like this:
// component.js
function ListItem(html, item) {
//const html = wire(item) // <- NOTE
function render() {
return html`<li>${item.foo}</li>`
}
return render()
}
function List(html, itemHtmls /* :( tried to be consistent */, items) {
//const html = wire(items) // <- NOTE
function render() {
return html`<ul>${items.map(function(item, idx) {
return ListItem(itemHtmls[idx], item)
})}</ul>`
}
return render()
}
Calling the components from the server:
// server.js
const {hyper, wire, bind, Component} = require('viperhtml')
const items = [{foo: 'bar'}, {foo: 'baz'}, {foo: 'xyz'}]
// wire for the list
const listWire = wire(items)
// wires for the children
const listItemWires = items.map(wire)
const renderedMarkup = List(listWire, listItemWires, items)
Calling from the browser would be the exact same, expect the way hyperhtml is imported:
// client.js
import {hyper, wire, bind, Component} from 'hyperhtml/esm'
However it feels unpleasant to write code like this, because I have a feeling that the result of the wire() calls should live inside the component instances. Is there a better way to write isomorphic hyperHTML/viperHTML components?

update there is now a workaround provided by the hypermorphic module.
The ideal case scenario is that you have as dependency only viperhtml, which in turns brings in hyperhtml automatically, as you can see by the index.js file.
At that point, the client bundler should, if capable, tree shake unused code for you but you have a very good point that's not immediately clear.
I am also not fully sure if bundlers can be that smart, assuming that a check like typeof document === "object" would always be true and target browsers only.
One way to try that, is to
import {hyper, wire, bind, Component} from 'viperhtml'
on the client side too, hoping it won't bring in viperHTML dependencies once bundled 'cause there's a lot you'd never need on the browser.
I have a feeling that the result of the wire() calls should live
inside the component instances.
You could simplify your components using viper.Component so that you'll have render() { return this.html... } and you forget about passing the wire around but I agree with you there's room for improvements.
At that point you only have to resolve which Component to import in one place and define portable components that work on b both client and server.
This is basically the reason light Component exists in the first place, it give you the freedom to focus on the component without thinking about what to wire, how and/or where (if client/server).
~~I was going to show you an example but the fact you relate content to the item (rightly) made me think current Component could also be improved so I've created a ticket as follow up for your case and I hope I'll have better examples (for components) sooner than later.~~
edit
I have updated the library to let you create components able to use/receive data/items as they're created, with a code pen example.
class ListItem extends Component {
constructor(item) {
super().item = item;
}
render() {
return this.html`<li>${this.item.foo}</li>`;
}
}
class List extends Component {
constructor(items) {
super().items = items;
}
render() {
return this.html`
<ul>${this.items.map(item => ListItem.for(item))}</ul>`;
}
}
When you use components you are ensuring yourself these are portable across client/server.
The only issue at this point would be to find out which is the best way to retrieve that Component class.
One possible solution is to centralize in a single entry point the export of such class.
However, the elephant in the room is that NodeJS is not compatible yet with ESM modules and browsers are not compatible with CommonJS so I don't have the best answer because I don't know if/how you are bundling your code.
Ideally, you would use CommonJS which works out of the box in NodeJS and is compatible with every browser bundler, and yet you need to differentiate, per build, the file that would export that Component or any other hyper/viperHTML related utilities.
I hope I've gave you enough hints to eventually work around current limitations.
Apologies if for now I don't have a better answer. The way I've done it previously used external renders but it's quite possibly not the most convenient way to go with more complex structures / components.
P.S. you could write those functions just like this
function ListItem(item) {
return wire(item)`<li>${item.foo}</li>`;
}
function List(items) {
return wire(items)`<ul>${items.map(ListItem)}</ul>`;
}

Related

How to pass the set[State] function to a non-descendent trigger component

Here is the diagram. ChildComponentB has a state - stateX. In ChildComponentA, once the event occurs, it will change the stateX in ChildComponentB.
If the ChildComponentA is the child component of ChildComponentB, then it's easy, just pass the setStateX as a prop to ChildComponentA. But in this case, it's not.
The real scenario is the following. I have a canvas component, there are some static Rectangles already there, once there are mouse move over the line of the Rectangles, I'd like to add the indicator lines to another child component of the canvas component.
Hence, the rectComponent is not the descendent of the distanceIndicatorsComponent. So I can't pass the setLines to RectComponent.
What's your approach to do that?
If I use useContext approach, will it work?
Thank you, #KonradLinkowski to provide your solution. Here is his code. However, useContext is still lifing the state up to ParentComponent.
import React, { useContext, createContext, useState } from "react";
const Context = createContext();
function ChildComponentA(props) {
const { setStateX } = useContext(Context);
return (
<div>
componentA button:{" "}
<button onClick={() => setStateX((i) => i + 1)}>add</button>
</div>
);
}
function ChildComponentB(props) {
const { stateX } = useContext(Context);
return <div> stateX is {stateX} </div>;
}
export default function ParentComponent(props) {
const [stateX, setStateX] = useState(0);
return (
<>
<Context.Provider value={{ stateX, setStateX }}>
<ChildComponentA> </ChildComponentA>
<ChildComponentB> </ChildComponentB>
</Context.Provider>
</>
);
}
Regarding the reusbility of the ComponentB i.e. distanceIndicatorsComponent in this scenario, it includes the JSX and the states plus the interface in which there are logic to change the states. The are all parts which should be reusable in the furture.
From OOP perspective, the lines (state) belongs to DistanceIndicatorsComponent, and the how to change the lines (Add Line in this case) should be also reusable logic which belongs to distanceIndicatorsComponent.
However, from React perspective, to lift the setLines (this is the interface triggered under some event) is not "good enough" from OOP perspective. To lift the state - lines and state management function - setLines up to CanvasComponent is a "not good enough" in terms of the encapsulation. Put a wrap component on top of ComponentB is the same thing, the setLines still can't be passed to FrameComponent unless FrameComponent is a child-component of the wrap component.
It's very common to see there is a very heavy component holding all the state and the events at the top. It makes me feel that's a bad smell of the code. The reusability of the component should be based on a set of components, in this set of components, there is one uncontrolled component at the top, and underneath of this uncontrolled component are controlled components. This set of components is a external reusability unit.
Here, in this diagram, there should be more than one reusable unit rather than one. If lift the state up to CanvasComponent, it makes all the components underneath are un-reusable. In some extents, you still can re-use the JSX of this component, but I'd say, in terms of reusablity, it should invovle as many reusable logic as possible.
I might be wrong, please correct me. And thank you for sharing your valuable comments.
Requirements
First let us sum up the requirements.
Rect Component and Distance Indicators have not much to do with each other. Making them aware of each other or creating a dependency between them would be not desired in a good OOP design.
The interaction between both is very specific. Establishing a mechanism or a data structure just for this special sort of interaction would add an overhead to all components that don't need this sort of interaction.
General Concepts
So you must use a mechanism that is so generic that it does not add any sort of coupling. You need to establish something between these two components, which only these two components know and which for all the rest of your program is nonsense. What mechanisms serve for such a purpose?
Function pointers
Lambda functions
Events
Function pointers and lambda functions are complicated constructs. Not everybody prefers to use them. Now you see why events are so popular. They address a common requirement of connecting two components without revealing any of the details of them to anybody.
I personally recommend you to use lambda functions in this situation. Because this is one strength of JavaScript. Search in google for callback or asynchronous lambda function. This often adds the least overhead to existing code. Because a lambda functions has an important property:
With lambda functions you can do things very locally. Doing things locally is an important design principle. You don't need to define extra methods or functions or classes. You can just create them wherever you are, return them, pass them freely around to where you actually need them and store them there. You can store them even without knowing what is behind them.
I think, this is your answer. The only thing you need is a mechanism to pass lambda functions and to store your lambda functions. But this is on a very generic level and therefore adds no coupling.
With events you are on similar path. The event mechanism is already there. But therefore you already have a good answer.
Example with pure JavaScript
When applying this to JavaScript we can imagine that function pointers could be compared to function expressions in JavaScript. And lambda functions can be compared to arrow functions in JavaScript. (Note: Arrow functions also provide "closures", which is required in this case, see How do JavaScript closures work?).
A simple example illustrates this:
class DistanceIndicator {
constructor(height, width) {
this.height = height;
this.width = width;
}
resize(height){
this.height = height;
}
incorrect_resizer(height){
return this.resize;
}
resizer(){
return (height) => this.resize(height);
}
resizer_with_less_overhead(){
return (height) => this.height = height;
}
}
p = new DistanceIndicator();
p.resize(19);
// If you want to use this, you have to store p. You may see
// this as not so nice, because, you are not interested in what
// actually p is. And you don't want to expose the information
// that it has a method resize. You want to have the freedom
// of changing such details without the need of changing all
// the code where something happens with Rectangles.
console.log(p.height);
resizer = p.incorrect_resizer()
//resizer(18);
// In this example, resizer is a function pointer. It would be
// nice to store it and be able to call it whenever we want to
// inform Rectangle about something interesting. But it does not
// work because the resize method cannot be isolated from the
// class. The "this" is not there.
console.log(p.height);
resizer = p.resizer();
resizer(17);
// That works. Lambda functions do the job. They are able to
// include the "this" object.
console.log(p.height);
resizer = p.resizer_with_less_overhead();
resizer(16);
console.log(p.height);
// As you have now a resizer, you can store it wherever you want.
// You can call it without knowing what is behind it.
The idea in the example is that you can store the resizers wherever you want without knowing what they are. You shouldn't name them resizer, but give them a generic name like size_notification.
Example for React
The React concept for contexts is a typical candidate for data exchange between components. But the principle of React is a pure unidirectional data flow (top-down). This is also true for the context, which means, we cannot use a context for what we want.
React does not provide support for the implementation of the proposed idea. React is only responsible for the pure construction of the HTML page and a comfortable and performant rendering. It is not responsible for the "business" logic of our HTML page. This is done in full JavaScript. That makes sense because you want be able to develop complex web applications. Therefore you need all your favourite programming concepts. A real application does not follow the design principle of React. React is only a presentation layer. Most people like OOP progamming.
So when implementing something with React we must keep in mind that React is just a library for JavaScript. The full power of JavaScript is always available and should be used for our web application.
After realizing this, the problem becomes simple. See this code:
import React from 'react';
let sizeNotificator = (newValue) => {console.log(newValue)};
function Rect(props) {
return <button onClick={() => sizeNotificator("12")}>resize to 12</button>;
}
class DistanceIndicator extends React.Component {
state = {
size: "0",
};
setSize(newValue) {
this.setState({
size : newValue
});
};
componentDidMount(){
sizeNotificator = ((newValue) => {this.setSize(newValue);})
}
render() {
return <p>Current size: { this.state.size}</p>;
}
}
class App extends React.Component {
render() {
return(<div>
<DistanceIndicator/>
<Rect/>
</div>);
}
}
export default App;
With this code the requirement is fulfilled that none of the DistanceIndicator implementation details are revealed to the outside of DistanceIndicator.
Obviously this example code only works if there is not more than one DistanceIndicator. To solve this is a different topic with probably not only one good solution.
If keeping the shared state in the ParentComponent is the problem, you can extract the Context.Provider to a separate component and pass components as it's children, those children can access the context value via useContext hook.
function ParentContextProvider({ children }) {
const [stateX, setStateX] = useState(0);
return (
<Context.Provider value={{ stateX, setStateX }}>
{children}
</Context.Provider>
);
}
export default function ParentComponent(props) {
return (
<ParentContextProvider>
<ChildComponentA />
<ChildComponentB />
</ParentContextProvider>
);
}
Now you can add any new state/setState to the ParentContextProvider and can pass it to it's children
Have you looked at Redux stores? You could have a variable like "showLine" or "originX"/"originY", then have one child dispatch changes, and the other child useSelector for the values?
Do you know if Redux works for your use case?
I prefer to use a simple events pattern for this type of scenario. Eg using a component such as js-event-bus.
CHILD COMPONENT A
props.eventBus.emit('MouseOverRectangle', null, new MyEvent(23));
CHILD COMPONENT B
useEffect(() => {
startup();
return () => cleanup();
}, []);
function startup() {
props.eventBus.on('MouseOverRectangle', handleEvent);
}
function cleanup() {
props.eventBus.detach('MouseOverRectangle', handleEvent);
}
function handleEvent(e: MyEvent) {
// Update state of component B here
}
RESULTS
This tends to result in quite clean encapsulation and also simple code. Eg any React conponent can communicate with any other, without needing to reveal internal details.

Is it a good idea to use React.Context to inject UI-Components?

I plan to build a react component library. The react components are UI-Components but should only implement a specific logic. I want the user to be able to define a set of atoms (basic react components) that are used to compose the actual components. My main goal is to make the library independent of a specific UI-Component-Library like MaterialUI, ChakraUI, etc.
My idea was to use a React.Context to inject the components like this:
// Button Atom
const Button: FC = ({ children }) => (<button>{children}</button>)
const atoms = { button: Button }
const AtomContext = createContext(atoms);
// "higher" component
const HigherComponent: FC = () => {
const atoms = useContext(AtomContext)
// Logic ...
return (
<atoms.button>click me</atoms.button>
)
}
export default function App() {
return (
<AtomContext.Provider value={atoms}>
<HigherComponent />
</AtomContext.Provider>
);
}
This solves my problem. But I'm not sure if it is a good idea. Are there better ways to inject UI-dependencies? What may be problems with my approach?
This is a question that can result in multiple answers based on personal experience.
But overall the idea to pass component trough context is a bit of misuse of that concept.
Also it does not bring much benefit from making a standalone library that can be imported via package.json or making a folder with components and importing them.
If you need to pass some specific things to your components you can have a custom provider as you did there, but as far as the component themselves, there is no common sense to use context.
If you end goal is to shorten the list of imports of component with custom context hook and just getting them like that, I think that is a bad tradeoff overall.
I just read this article about using react context for dependency injection. The authors opinion is, that react context for injecting non-react dependencies into components is good practice. However, I'm not sure if that applies to injecting a specific set of react components. Since, react components are nothing more than functions I think it should be alright to use reacts context for that.

How to create and call selectors from a separate file in Cypress?

I am new to Cypress and struggling to get it work. I need to create a file with selectors (I suppose in 'support' folder) to use them in my project file.
Here is an example
describe('Test_spec_1', () => {
it.only('Visits the site & verifies elements', () => {
cy.get('[type=text]').should('be.visible')
cy.get('[type=password]').should('be.visible')
cy.get('[type=submit]').should('be.visible')
cy.get('[routerlink="/login"]').should('be.visible')
cy.get('[routerlink="/reset-password"]').should('be.visible')
cy.get('[routerlink="/support"]').should('be.visible')
cy.get('[routerlink="/reset-password"]').should('be.visible')
})
})
Basically, I need to have all selectors in a separate file, so I can call them and update their values easily.
I have experimented a bit with export/import, but it did not work. Could't find anywhere how to use it properly. It would be great if you can give me some hints how to do it. Thank you very much.
You can create a folder called page-objects inside your integration folder. Inside that you can create one js file for each screen like login.js. Now inside that you can write your locators like:
class login {
usernameInput() {
return cy.get('[type=text]')
}
passwordInput() {
return cy.get('[type=password]')
}
submitButton() {
return cy.get('[type=submit]')
}
}
export default login
Inside your tests you can use them as:
import login from '/page-objects/login.js'
const loginPage = new login();
describe('Test_spec_1', function() {
it('Visits the site & verifies elements', function() {
loginPage.usernameInput().should('be.visible')
loginPage.passwordInput().should('be.visible')
loginPage.submitButton().should('be.visible')
})
})
Please don't use page objects in Cypress, see this tutorial Stop using Page Objects
Page objects problems
Page objects are hard to maintain and take away time from actual application development. I have never seen PageObjects documented well enough to actually help one write tests.
Page objects introduce additional state into the tests, which is separate from the application’s internal state. This makes understanding the tests and failures harder.
Page objects try to fit multiple cases into a uniform interface, falling back to conditional logic - a huge anti-pattern in our opinion.
Page objects make tests slow because they force the tests to always go through the application user interface.
Number 3) killed it for me. You get to a point where you try to figure out more convoluted methods in the page object to cater for different scenarios.
The easiest way to store your selector text in one place is given in this question Where to store selectors in Cypress.io
// cypress/support/selectors.js
export default {
mySelector: '.my-selector',
mySelector2: '.my-selector-2'
};
// cypress/integration/one.spec.js
import selectors from '../support/selectors.js';
describe('test', () => {
it('test', () => {
cy.get(selectors.mySelector);
});
});

Advice about webapp architecture and reactJs

This question is more to know your opinions about the way I'm trying to solve this issue.
I would need a little bit of ReactJs expertise here as I'm quite new.
First a little bit of context. I'm developing a web application using ReactJs for the frontend part.
This webapp is going to have many translations, so for maintenance I thought it would be better to store all the translations in a database instead of having them into a file. This way I could manage them using sql scripts.
I'm using a MySQL database for the backend, but for performance reasons, I have added ElasticSearch as second database (well, it is more a full text search engine).
So once the application starts, the translations are automatically loaded into ElasticSearch. Every translation has a code, and a text, so in elastic search I only load the translations for one locale (by default english), and when a user switchs the locale, a call is done to load all the translations for the selected locale and update their corresponding text.
This way from the fronted I can reference a translation only by the code and I will get the text translated in the correct locale.
Now, how do I do that in react?
So far I have written a component TranslatedMessage which is basically looking for a given code and displaying it whereever this component is rendered.
Below the code of the component:
import React from 'react';
export class TranslatedMessage extends React.Component {
constructor() {
super();
this.render = this.render.bind(this);
this.componentDidMount = this.componentDidMount.bind(this);
this.state = {message: ''};
}
render() {
return (<div>{this.state.message}</div>);
}
componentDidMount() {
var component = this;
var code=this.props.code;
var url="data/translation?code="+code;
$.get(url, function (result) {
component.setState({message: result.text});
});
}
};
And then I use it in the application whis way, for example to translate the title of an 'a' link:
<TranslatedMessage code="lng.dropdown.home"/><i className="fa fa-chevron-down" />
So far is working fine but the problem is that I need to refresh the whole page to get the new translations displayed, because I'm not updating the state of the component.
So now my questions:
1)Every time that we find in a page the component TranslatedMessage, a new instance of that component is created right? so basically if I have 1000 translations, 1000 instances of that component will be created? And then React has to take care and watch all these instances for changes in the state? Would that be very bad for performance? Do you find any more efficient way to do it?
2) I don't think forcing the whole page to reload is the most proper way to do it, but how can I update the states of all that components when a user switch the locale? I've been reading about a framework (or pattern) called Flux, and maybe that could fit my needs, what do you thing, would you recommend it?
3) What do you think about storing translations on db, I'm a bit concern about sending a query to the db for every translation, would you recommend or not this approach?
Any suggestions, ideas for improvement are very welcome!
Thank you for taking your time to read it or for any help!
I use what is basically a Flux store for this purpose. On initialisation the application requests the whole language file to use (which is JSON) and that gets shoved into memory in the store, something like this (I'm going to assume a totally flat language file for now, and I'm using ES2015/16 syntax, I've omitted error checking etc etc for brevity):
class I18n {
constructor() {
this.lang = await fetch( 'lang_endpoint' )
.then( res => res.json() )
}
get( translation ) {
return this.lang[ translation ] || null
}
}
Somewhere my app starts during a ReactDOM.render( <App /> ) or some variation and this renders the whole thing top-down (I try to eliminate state as much as possible). If I needed to switch languages then I'd bind a change handler such that the store emits a change event which is heard by some code that triggers a ReactDOM.render. This is fairly standard Flux practise for changing the app state, the key is to try and eliminate state from your components and store it inside your stores.
To use the I18n class simply instantiate it somewhere (I normally have it as a singleton exported from a file, e.g. module.exports = new I18n(), require that file into your components and use the get method (this assumes some sort of packager such as browserify or webpack but it looks like you have that complexity all sorted):
import 'i18n' from 'stores/i18n'
class MyComponent extends React.Component {
constructor() { ... }
render() {
return (
<span>{ i18n.get( 'title' ) }</span>
)
}
}
This component could also be simplified to
const MyComponent = props => <span>{ i18n.get( 'title' ) }</span>

What's the best way to inject a React component into another React component? Or should I bother?

While working on my first large React application, I started feeling uneasy about the tight coupling of nested components, especially when writing unit tests.
If is a high-level component, and its render function returns <div><Bar><Baz /></Bar></div>, I would like to be able to test Foo, Bar, and Baz in isolation.
I searched for advice but didn't find any. So, on my own, I came up with two methods of injection: factory functions and via the outer component's properties. (I am not talking about Children. I'm talking about "baked in" dependencies--the sort people usually import via require or import statements.
Properties
const Baz = React.createClass({
render() {
return <p>Inner</p>;
}
});
const Foo = React.createClass({
render() {
const Bar = this.props.innerComponent;
return <Bar />;
}
});
ReactDOM.render(
<Foo innerComponent={Baz} />,
document.getElementById('container')
);
Factory
const Baz = React.createClass({
render() {
return <p>Inner</p>;
}
});
const fooFactory = innerComponent => {
return React.createClass({
render() {
const Bar = innerComponent;
return <Bar />;
}
});
};
const Foo = fooFactory(Baz);
ReactDOM.render(
<Foo innerComponent={Baz} />,
document.getElementById('container')
);
Or am I just taking decoupling too far? I see almost no one else doing this in any tutorials or examples.
Would you be inclined to inject components sometimes but not other times? In what circumstances? And, if you did it, would you use one of the above techniques or do it some other way?
This is a pattern you'll see from time to time when there is a run-time necessity for specifying a component type which the component it's passed to will instantiate itself; for example, the React TransitionGroup component takes such a property called component:
By default ReactTransitionGroup renders as a span. You can change this behavior by providing a component prop. For example, here's how you would render a <ul>:
<ReactTransitionGroup component="ul">
...
</ReactTransitionGroup>
Every DOM component that React can render is available for use. However, component does not need to be a DOM component. It can be any React component you want; even ones you've written yourself! Just write component={List} and your component will receive this.props.children.
It's less common to use this pattern for the user-specified contents of a component, since this is solved more elegantly by this.props.children.
However, as far as testing is concerned, the common solution is simply to export each component separately and test it in isolation. If a high-level component composes them in a certain way, all you really need to test is that they are composed properly, since the components are also tested individually.
There is a common pattern of separating "smart" and "dumb" components; dumb components simply render what they're given, and are very easily unit tested. Smart components may fetch data (e.g. via Ajax, or from a flux store), compose specific dumb components, or make other "decisions," and can be tougher to test—but again, since the dumb components are already tested, ideally you can just check that the smart component behaves correctly and renders the right thing.
To use your example, it's Foo's job to render <div><Bar><Baz /></Bar></div>; it's a smart component, and the test should ensure that it renders exactly that. Bar's job, however, is to render its children, and is more easily tested.
You also have the ability to mock out a component with TestUtils.mockComponent.
Check out mochajs. This testing framework overrides import / require, which solves your problem.
It does mean that your subcomponents need to be written as ECMA 6 or NodeJS modules, though - but that really just means they need to be in separate source files.

Categories

Resources