How to replace a React component - reactjs

I plan to have a base app where most of the components should be replaceable at a later time, depending on specific customer needs without touching the base app code. What is the best mechanism to achieve this within the React ecosystem? Is there a registry that can be updated, or any other deferred binding of components similar to the GWT replace-with construct? Thanks.
Update 1:
My understanding is that Dependency Injection would allow one to inject into static components. The question I'm asking is more about whether it's possible to implement a registry of sorts and compose the application via component registry keys, thus enabling later updates to specify which component that key will be resolved to. React seems to expect a static relation there, and I want it to be dynamic if possible.

No, there's no central registry, but it wouldn't be too difficult to create one. Something like (sorry about mixing JS syntaxes)
// Registry module
var Component1 = require('component1')
var components = { Component1 }
var replace = function(name, cfn) { components[name] = cfn }
// Render module
var registry = require('registry')
var Component1 = registry.components['Component1']
render...Component1
// Override module
var registry = require('registry')
var Component1override = require('component1override')
registry.replace('Component1', Component1override)
The only trick is to make certain that the Override module gets loaded before the Render module.

Related

Global es6 module to store constants in react app - prevent garbage collection

I would like to store some variables inside a module (export) to be used as constants though out my react app. I would like to avoid context because there is no need for components to re-render and also I need those constants to be used outside my react components.
Where should I do it (where to import it), in order to prevent garbage collection?
One idea I have is to import and re-export it on top of my root component.
EDIT:
To be more precise, there will be a component that will set the constant once (mutate the variable), so that other components or files can access it.
So, what you will need is some sort of setter/getter pattern. Though I mostly don't recommend it unless you know what you are doing, because React won't re-render if the variable changes and because of that you need to be sure the variable is set before it is used.
You should have something like the example below in order for it to work the way you want. You can find an example of it working on this Codesandbox.
export let MY_VARIABLE = "";
export const setMyVariable = value => (MY_VARIABLE = value);
PS: I've added some console.log to the code in order for you to see how the import/get/set behaves.
After digging more into this I found that es6 module spec states:
When import your module it gets loaded => parsed => evaluated and cached (singleton). It also says that when you import modules its value is passed by reference (aka assignment). I didn't find anything mentioning when or how es6 modules are unloaded from that cache.
So that means, when you import a module once, it is there for as long as the program is running, and all modules access its values directly.
reference
https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/
https://medium.com/#mivanichok/understanding-es6-modules-in-depth-article-b49612926e39
You can create an config.js inside src folder and write the your constant variable like
//config.js
module.exports = {
CONST_VAR : 'const value',
}
import config.js in your component and use it

Sharing data (an array) between two screens in tabs view using react-navigation in react native

I am using react-navigation without redux. so i have two tabs each with their own stack navigator, having one screen each. so i need and array of locations in both screens. currently i am doing this in both screens:
state = { locations: [] };
componentDidMount() {
this.getAllLocations();
}
async getAllLocations() {
let locations = await this.getMoviesFromApi();
this.setState({ locations });
}
i just want to have this array at one location and both components should share this single source of truth. so changes made by either screen is reflected in the other screen. Is this possible without redux?
RN 0.59 has opened great possibilities with its release. One of them are react hooks, which is available in the latest version... in the future react hooks will be used everywhere. Trust me. So, a while back I looked for the possibilities of having a global state using react hooks and found the reactn library. It uses react native hooks, and even you can use global state in CLASS components. which opens a new door for theming and sharing data. Now my app supports light/dark mode, dynamic font size, Languages, and early implementation of "portals" using only this library.
The best part about it is that you can use it like state. There is no need of provider, or redux stuff (although it provides it). It can be integrated with react navigation (it requires modifying some source code, at most, adding an "n" to react, and reference the global variable). Is awesome and I love it.
I have been thinking in doing an article on medium about this, because the lib is not that popular in RN community, but hope that you will give it a chance the library is only 22KB, less than one full component.
As an alternative, you could think about writing your own library using hooks. But it's gonna be hard. Try it, there is no going back
It is possible if you have a singleton object :
export default class SharedData {
constructor(){
if(SharedData.instance){
return SharedData.instance;
}
this.state = {locations:[]};
this.listners =[];
SharedData.instance = this;
return SharedData.instance;
}
setLocations(locations){
this.state.locations = locations;
this.listners.forEach(listner=>listner(this.state.locations));
}
getLocations(){
return this.state.locations;
}
addListner(listner){
this.listners.push(listner);
return listner;
}
removeListner(listner){
let index = this.listners.indexOf(listner);
if(index > -1){
this.listners.splice(index,1);
}
}
}
and then in every tab where you want to access shared locations state:
// get an instance of SharedData
this.sharedData = new SharedData();
// subscribe to locations changes
this.listner = sharedData.addListner((locations)=>{
this.setState({locations});
});
// set locations
this.sharedData.setLocations([]);
// unregister when destroying the component
this.sharedData.removeListner(this.listner);
I guess in order to achieve your goal, you're going to need some sort of a mechanism for storing 'global data', and if you don like Redux because it requires a lot of setup to achieve this simple task of sharing global data, then you chould you unstated ... which is alot simple to setup

React redux oop classes

coming from angular i used to have a class for every entity in my DB, such class encapsulated all entity behaviour.
for example users Class can look like
export class User{
static notValid(u){
return !!((u.id && u.id > 0 && u.fullname && u.fullname.length > 2 && u.picture) === false);
}
static fromArray(arr){
let pack = [];
for(let i=0;i<arr.length;i++){
pack.push(new User(arr[i]));
}
return pack;
}
constructor(u){
this.id = u.id || 0;
this.fullname = u.fullname+'' || 'N/A';
this.picture = u.picture+'' || '/imgs/logo.png';
this.role = u.role || 'N/A';
this.username = u.username+'' || '';
this.email = u.email+'' || '';
this.dob = u.dob || 0;
this.gender = u.gender+'' || '';
///START SETTING FLAGS AND VALIDATING DATA;
this.isValid = !User.notValid(this);
this.saved = this.id > 0;
let n = this.fullname;
this.nickname = n.split(' ').shift()+' '+n.split(' ').pop();
}
save(){
///IF NO ID, POST TO SERVER
if(!this.saved)return $http.post('/user',this.toJson);
return $http.put('user/'+this.id,this.toJson());
//tojson is defined in prototype;
}
identity(){
return {id:this.id,fullname:this.fullname,picture:this.picture,nickname:this.nickname};
}
}
}
so that my controller doenot know about how to save or update User, all it have is to trigger save() on user object.
Now React world, where every thing inside app is a component;
1. how can i replicate such approach inside react component ?
i read alot that there is presentational components and smart components. but what about Data Model component ?
2. if i port all my current class's to react should i also implement render method ? can i have multiple render functions to return different html based on page.
example above User can appear inside Profile will all details, and as a card in users list, so i should keep html for both inside class prototype ?
You seem to be a bit confused about React and what it is designed to do which is perfectly normal, coming from the Angular world.
The thing is, as far as React is concerned there is no such thing as a data model, only components. These components can have state (or they may not) and these components are rendered to the DOM.
Various types of components seem to have confused you as well. React is only concerned with how data is presented. Presentation and container components are distinguished from each other to make it easier for us to reason about how to manage application state.
To answer your specific questions:
1) If you are really adamant about keeping your existing structure and make it work with React, you don't actually need to do a lot of work. Your data model components are just JavaScript objects. You can pass them around and you can give them to components. When some event happens in the components, you can call the relevant methods in your objects. You will need to make sure that Angular specific methods are ported to pure JavaScript though.
I advise against this approach. It will work at first but you will find yourself in a maintenance hell in no time. Believe me, I'm building large scale React apps in my job and I have been bitten by similar decisions when I first started writing React components.
2) Certainly you could add a couple of React methods to your class definitions and also throw in the presentation code (that is, HTML) and presentation state. Then you would be able to render these components.
But that really is not the way to go about it. React doesn't decide on anything for you whereas Angular is very opinionated about this. First and foremost you should follow some tutorials on React. Looks like you have a sizable application in your hands, so I would advise you to look into Flux and Redux as well.
After that you should sit down and design how your application should work and how your state should look. After that it will be a breeze to go through the actual coding part.
You can NOT have multiple render methods in a React component, that makes absolutely no sense. React is pure JavaScript and JavaScript doesn't have any concept of overriding class members in the sense of classical OOP. Heck, JavaScript doesn't even have the concept of a class, that has been included in ES6 so people coming from class oriented programming languages wouldn't need to properly learn how the prototype chain works.
React components, and JavaScript objects in general, can only have one key. You can try this in your browser's console.
let a = {b: 1, b: 2};
console.log(a);
The object a will only have one b key and the value for that key will be 2.
Having said all this, you can delegate the actual rendering of a component to other objects based on some conditions using normal JavaScript coding methods. But this isn't how React is supposed to work. Your render method can be able to decide on what to render based on your conditions.
First of all, let me tell you that I can't answer your question.
It looks like you're new to React. I've never used this approach to create a class for every entity in DB, in React, ever. It's new to me. Maybe it works, maybe it doesn't. But what I'd suggest you is to get your hands dirty first with example projects in react. That'll answer most of your questions.
However, I can answer some of your questions-
Data Model component ?
Obviously, there is no such thing as Data Model component. React is all about unidirectional data flow. You want to use redux for state management in react. The components which are connected to this state are connected/smart components. Smart components pass the state to presentational/dumb components via props (properties). So there is that. All of the state comes from Redux or similar state management mechanism viz. Flux.
can i have multiple render functions to return different html based on
page.
No. One component contains only one render() method. That is precisely why I suggest you to please build some example apps in React.
If you want to learn React, here's what I'd recommend you, in that particular order-
React.js
Redux
Redux-thunk
Redux-saga
React is not a monolithic framework like Angular. It's just a library. And programmers are meant to throw different libraries together to build their apps.

How React Component expose data to external app?

Let's say i have a React Component <forecast id="test"/>. And i want import this component into a legacy project which only have jquery involved.
Is it possible to get the value of this component like document.querySelector('#test').value?
I got some information from React website, that we cannot access data from outside the component. The recommended way is dispatching data from inside of the component.
My question is, the way to dispatching data is behind of the component implementation. Is it means that i have to read the source code of component in case i don't know how it works?
If this is true, i won't think React is free to inject to any product, it cost too much.
If you want to inject some React to your project you should do it with some independent part of your system.
If you have tightly coupled code base its always high cost to add any new technology to it. So its not a React problem. Try to find some independent module or subapplication in your system and move it to React. If you cannot find one, try to refactor existing code first.
You need to write a plain JS wrapper to do it. Something like this might work
function Forecast(element) {
this.value = initialValue;
React.render(<forecast onChange={onChange.bind(this)}/>, element);
function onChange(newValue) {
this.value = newValue;
}
}

Stable reactid for server-side rendering

When using React to render components on the server, I notice that the data-reactid attributes are effectively random. I understand that's expected. (https://groups.google.com/forum/#!topic/reactjs/ewTN-WOP1w8)
However it's a little surprising that this otherwise functional framework introduces such non-determinism in the view output. It means that successive renderings of a view with identical state will create different HTML, preventing, for instance, the view engine from returning a '304 Not Modified' or generating a reliable ETag. (I appreciate such caching could be handled at a higher infrastructure layer as well.)
Is there a way to seed the generation of the identifier so that the reactids are deterministic? Or is the reason that's a bad idea explained somewhere else?
In the final comment on the Google Group thread Ben Alpert says:
For server rendering, it's important that different rendered components don't have colliding IDs (even if they're rendered on different servers, for instance) so we pick them at random.
Also recently thought about it(just started to use reactjs),
Possible solution is quite simple - there is no requirement for ETag to be generated from real html... - it can be generated from displayed data.
So you can generate it from virtual dom - just use React.renderComponentToStaticMarkup(…) and generate ETag from it...
Or you can remove all reactid's from rendered html with regexp before hashing it(likely faster then separate render)...
In case if you are using express, this would be something like:
var virtualDom = React.createFactory(Handler)({});
var html = React.renderToString(virtualDom);
var etag = app.get('etag fn');
if (etag) {
etag = etag(React.renderComponentToStaticMarkup(virtualDom), 'utf8');
etag && res.set('ETag', etag);
}
res.render( ... );
This was bugging me too, so I did some digging to see what would happen if I made the root reactid deterministic.
It is possible override this in React 0.14.x if you're willing to put up with the following hack. In your server-side file where you call ReactDOM.renderToString, place this at the top:
// Override the ServerReactRootIndex.createReactRootIndex function
var ServerReactRootIndex = require('react/lib/ServerReactRootIndex');
ServerReactRootIndex.createReactRootIndex = function(){
return "x"; // Results in an attribute like data-reactid=".x"
};
// Use React as usual
// NB: require('react') must come AFTER overriding ServerReactRootIndex.createReactRootIndex
var React = require('react');
This isn't part of the React API, so this may break in the near future. However, this works for now if you absolutely need it. It also makes the data-react-checksum attribute stable for the same rendered DOM.
If you have multiple root React components in the one page, they MUST have different root ids, so you will need to modify this function to account for this.

Resources