How React Component expose data to external app? - reactjs

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;
}
}

Related

ReactJS - What are the pros and cons of managing context outside of the component tree?

In a React (Native) application, consider a util file such as:
let apples = 0;
export function addApple() {
apples++;
}
export function getApples() {
return apples;
}
Is it okay to store the state of apples this way such that components can modify it like:
import { addApple } from "./myUtil";
export function AddAppleButton {
return <button onClick={addApples} />
}
And non-React code can use it like:
import { addApple } from "./myUtil";
export function addMultipleApples(numberOfApples) {
for (let i = 0; i < numberOfApples; i++) {
addApple();
}
}
What are the pros and cons of managing context this way?
Essentially, it won't work. Since apples is not managed by react state, when it changes, React will not trigger a re-render so the value will not update on screens that read from it. It may accidentally look like it's working if a re-render happens for some other reason, React will pick up the new value -- but this would be extremely fragile and inevitably lead to indeterminate states and race conditions you don't want to exist.
For what you need to do, it would make more sense to build a store container (like a class) and put this in the React state, then add methods to that store such that its values can be read and written from the outside. The store would need to inform components listening to it the value changed, via a subscription mechanism or similar. This is a fairly complex thing to set up, so it'd probably be better to adopt a library that supports reading/writing stores from outside of React. Like Zustand.

Third-party library as React's source of truth

I wonder how to work with React when I am forced to use a third-party library that owns data. I want to consider it a source of truth, but the library is neither aware of React nor has an immutable state. As an example, consider the following todo-list as the third-party library I want to preserve untouched.
class TodoList {
items = ["dishes", "trash", "shop"];
addRandomItem() {
this.items.push(Math.random().toString());
}
}
I want to achieve a situation where clicking a button adds a new item and re-renders the list.
Please consider this as a simple example. The best would be some kind of interlayer handling both function calls and components updates but I am curious how this problem is solved in other projects if occurs.

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.

Re-rendering an app using ReactDOM.render() rather than within its container component -- anti-pattern?

I have a single object state-driven application whose state dispatch/subscribe logic is to be kept separate from the React 'flow' (i.e. no helpers like React-Redux bindings).
When the state changes, my app re-renders.
Is there any difference between the following two implementations, or any anti-pattern concerns? (sorry to anyone upset I'm not using JSX)
var myElementClass = React.createClass(
render : function() {
//make use of this.props.state...
}
);
var myAppContainerComponent = React.createElement(
myElementClass,
{state : dataStore.getState()}
);
dataStore.onChange(function(){
ReactDOM.render(myAppContainerComponent, someDOMContainer);
});
vs...
var myElementClass = React.createClass(
componentDidMount : function() {
var self = this;
this.props.store.onChange(function(){
self.setState(self.props.store.getState());
});
},
render : function() {
//make use of this.state...
}
);
var myAppContainerComponent = React.createElement(
myElementClass,
{store : dataStore}
);
ReactDOM.render(myAppContainerComponent, someDOMContainer);
The first forces the app-wide re-render from 'outside', i.e. using ReactDOM. The second does the same thing within the container app.
I've done some performance tests and don't actually see a difference. Will I run in to issues down the road? Is hitting ReactDOM.render() many times an issue?
I know some people will comment that both ways are possibly expensive as they're each re-rendering the whole app (isn't that what React is for ;) ), but that's out of scope of this question.
There is no big difference when you have a few components, but when your app grows large, re-rendering from the top is going to introduce a slowdown. This is why I would recommend subscribing individual components to the store and only using setState() if the part of the state that they care about has changed. This way your components will be much more performant as the app grows.
Finally, we don’t recommend you to use store.subscribe() directly. There is a whole library called React Redux which does the subscription for you! When you use connect() from it, it wraps your components with that setState() logic so you don’t have to write it, and you only need to specify the parts of the state that your components care about. Also, React Redux is more efficient than the code you would write by hand because it contains many optimizations.
I think your components should be a pure function of whatever state happens to be outside of it, but not aware of that state (well, as "pure" as it can reasonably be).
I see a "leaky implementation" here in the second example, meaning that when you have:
componentDidMount : function() {
var self = this;
this.props.store.onChange(function(){
self.setState(self.props.store.getState());
});
},
you are conflating the component itself with the function that is supposed to cause re-rendering of the component.
Your first implementation seems a lot more appropriate to me. Additionally, the first implementation is a lot more re-usable.
In your second example, what if you want to change the structure of your data store, rendering, etc.? Then you may very well have to go into every single component and change it as well.
Bottom line, I definitely like the first implementation better.

Resources