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.
Related
This is not to start a discussion about Anugular vs REACT. I know at some point I will learn to love REACT. That is why I have started doing new apps in REACT. I just need some kind of confirmation whether I understand REACT correctly according to bindings.
I am making a function REACT component (arrow function) as I can read this is the new recommended practice. I have some data with an array of data with arrays of data. I am using all of this data to show and change content. In this specific sample I use it to toggle some menu's.
In Angular in the toggle you would have something like:
toggle(row){
row.toggle=!row.toggle;
}
As I understand it in REACT, if you want to change content, or have some states about toggle you need hooks (correct me if I am wrong). So in REACT you would apply all of your data to a setData(someData). Something like below:
const toggleMenu = (section, row) => {
console.log(section);
setDataList(
dataList.map(s => {
return s.id === section.id
? {
...s,
table: s.table.map(t => {
return t.id === row.id
? { ...t, toggleDropdown: !row.toggleDropdown }
: t;
})
}
: s;
})
);
};
So I have to find my specific objects in my data, update it, and pass it all back to the hook setter. I have two concerns here. Sometimes I meet customer requirements where they want tons of data on a home page (even when it is not best practices). That will still perform with Angular. Anyone has some experience whether REACT will perform in such scenarios with this approach (so much looping)?
Last have I misunderstood something, or is this normal practice when working with REACT - or have I over complicated the task? I have made a small sample here to show a sample case:
https://codesandbox.io/s/dropdown-for-multiple-maps-gmqpp?fontsize=14&hidenavigation=1&theme=dark
The answer I am looking for is Yes this is the way to go, or no you have totally misunderstand the concept because .. And a plus if somebody have experince with the performance for pages with a lot of content.
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
I've recently built a few isomporphic/univeral projects using the React-Redux-Express-Mongoose stack.
In my mongoose models is contained a lot of business-logic. As a very basic example (excuse my ES6):
import mongoose, {Schema} from 'mongoose';
const UserSchema = new Schema({
name: String,
password: String,
role: String
});
UserSchema.methods.canDoSomeBusinessLogic = function(){
return this.name === 'Jeff';
};
UserSchema.methods.isAdmin = function(){
return this.role === 'admin';
};
This is all great on the server, however when these models are hydrated in the browser as plain JSON objects, I then have to re-implement this same business logic in some React component or Redux reducer, which doesn't feel very clean to me. I'm wondering how best to approach this.
From reading around Mongoose, there seems to be limited browser support, mostly just for document validation. I suppose my main options are:
Move all the business logic into some "normal" JS classes, and instantiate those all over the place. For example:
# JS Class definition - classes/user.js
export default class User {
constructor(data = {}){
Object.assign(this,data);
}
canDoSomeBusinessLogic(){
return this.name === 'Jeff';
};
isAdmin(){
return this.role === 'admin';
}
}
# Server - api/controllers/user.js
import UserClass from
User.findById(1,function(err,user){
let user = new UserClass(user.toJSON();
});
# Client - reducers/User.js
export default function authReducer(state = null, action) {
switch (action.type) {
case GET_USER:
return new UserClass(action.response.data);
}
}
# Client - containers/Page.jsx
import {connect} from 'react-redux';
#connect(state => ({user: state.user}))
export default class Page extends React.Component {
render(){
if(this.props.user.isAdmin()){
// Some admin
}
}
}
Move all the business logic into a some static helper functions. I won't write out the whole example again, but essentially:
# helpers/user.js
export function isAdmin(user){
return user.role === 'admin';
}
I suppose the difference between the above 2 is just personal preference. But does anyone have any other thoughts about isomorphic apps and data modelling? Or have seen any open-source example of people solving this problem.
As an extension to the above, what about an isomorphic save() function e.g. User.save(). So if called on the client it could do a POST to the relevant API endpoint, and if run on the server it would call the Mongoose save() function.
Spoiler: Expect an opinionated reply. There is no 'right' way to do it.
First of all, I want to make the difference between isomorphic and universal clear, so that you know exactly what we are talking about:
Isomorphism is the functional aspect of seamlessly switching between client- and server-side rendering without losing state. Universal is a term used to emphasize the fact that a particular piece of JavaScript code is able to run in multiple environments.
Is it worth it that much abstraction into an universal app?
Generally what you want an universal app for is to have the client and the server that pre-renders the app both loading the same code. Although you can run the API from the same server that pre-renders the app, I would rather proxy it and run it in a different process.
Let me show you two different React repositories:
React + API erikras/react-redux-universal-hot-example
React wellyshen/react-cool-starter
Erikras well-known boilerplate uses his universal app to share dependencies globally, and code between the server that pre-renders the page and the client. Although he could, he does not share validation. Survey API validation Survey client validation
Wellyshen does not have an API, but he also shares his dependencies and code, though only between the server and the client. The server loads the routes, the store and everything that is being run by the client app. That is to provide isomorphism.
Having said that, it is up to you whether to move all validation in one place. I probably would just consider it for complicated validation cases, like an email validation which you could actually have a helper for that. (that was just an example, for email validation you already have validator). In certain occasions, it might be more convenient to rely on the API validation, albeit not being the best practice.
Simple validations, like the ones in your examples, can be done effortless with redux-form anyway, which that I know there is no direct way to translate it on the API. Instead you should probably be looking for express-validator on it.
One more thing, despite the fact that a few very popular React boilerplates will have the API and client together, I tend to work with two different repositories: the React + server-side rendering and the API. In the long term run it will result in a cleaner code that will be totally independent one from the other. organizing-large-react-applications
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.
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;
}
}