I first heard of React when a friend showed me a project he had written. It was some sort of content management system: it had tables, forms, visual editors and stuff like this. I don't remember much, except that code was really really confusing, it looked like this:
// hint: you can use this codepen to follow along:
// https://codepen.io/valeriavg-the-flexboxer/pen/WNJNMRp
const app = React.createElement(
// tag
'button',
// properties
{
onClick:function(){alert("Hello!")}
},
// children
"Click Me!"
)
ReactDOM.render(app,document.getElementById('app'))
// <div id="app"></div>
So, of course, I was quite frustrated someone would want to write that, when you could just write this instead:
<button onClick="alert('Hello!')">Click me!</button>
JSX: HTML in JS
Some time passed, and, to my surprise, React was all over the place: every other job advertisement was mentioning it.
And so I gave it another try. This time around it wasn't just a library you import - somehow it turned into a whole new language, called jsx. Which, however, was vaguely familiar:
const app =
<button onClick={()=>alert('Hello, JSX!')}>
Click me!
</button>
ReactDOM.render(app,document.getElementById('app'))
That was almost the same as my old pal HTML, except JSX allowed splitting HTML pages into tiny reusable dynamic building blocks:
const One = () => <div> One </div>;
const Two = () => <div> Two </div>;
const Three = () => <div> Three </div>;
const app = (
<div>
<One />
<Two />
<Three />
</div>
);
ReactDOM.render(app, document.getElementById("app"));
Behind the scenes, however, it was still the same code:
const One = () => React.createElement('div',{},'One');
const Two = () => React.createElement('div',{},'Two');
const Three = () => React.createElement('div',{},'Three');
const app = React.createElement('div',{},One(),Two(),Three());
ReactDOM.render(app,document.getElementById("app"));
But JSX made a lot of difference and React has finally started making sense to me.
Stateful and stateless components
One of the first things I've learned was a "stateful" component:
class App extends React.Component {
constructor() {
super()
this.state = {
name: ''
}
}
render() {
return (
<div>
<h1>Hello, {this.state.name} </h1>
<input
type="text"
value={this.state.name}
onChange={(e) => this.setState({name: e.target.value})}
/>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("app"));
Or rather it was the second thing, as, apparently, I've already got familiar with it's "stateless" counterpart.
A stateful component had a state which was triggering a re-render on change, while the stateless had only the render part and were rendering exactly the same thing as long as props were the same:
class App extends React.Component {
render() {
return (
<div>
<h1>Hello, {this.props.name}! </h1>
</div>
);
}
}
ReactDOM.render(<App name="React"/>, document.getElementById("app"));
Back then I worked for a startup which allowed creators distribute video content for their fans among other things. That meant we had a dashboard for creators, and website and an app for end users. And React worked perfectly well for the dashboard, especially when functional components came along:
const App = () => {
const [name, setName] = React.useState("");
return (
<div>
<h1>Hello, {name} </h1>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("app"));
And so, the project was growing, along with the amount of dependencies. We used emotion styled components, react-router and of a little something to manage the state.
State management
One of the first libraries for state management I've tried was RxJS. Sure, it added even more magic to the project, but hey, it was cool to share state between two components, right?
Wrong, it was chaos! I could never tell which one of them changed the state and it made debugging quite mind bending, as sometimes console.log
has been printed a microsecond before the state has been actually propagated.
Redux has treated me a bit better in that sense, but having one gigantic store was not convenient for my preferred modular architecture.
And so I stuck to the React's own context
because I could easily split states and trace the updates easier:
const NameContext = React.createContext("");
const Name = () => {
const name = React.useContext(NameContext);
if (!name) return "";
return <h1> Hello, {name}!</h1>;
};
const App = () => {
const [name, setName] = React.useState("");
return (
<NameContext.Provider value={name}>
<div>
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="What's your name?"
/>
<Name />
</div>
</NameContext.Provider>
);
};
ReactDOM.render(<App />, document.getElementById("app"));
Which, as you can tell from the code, was precisely around the time functional components came along.
Functional components
In a nutshell, functional components were an attempt to turn stateful components into stateless with ease and vice versa by letting them all be functions and use hooks instead:
const App = () => {
const [name, setName] = React.useState("");
return (
<div>
<h1>Hello, {name} </h1>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("app"));
I can't argue that code became much easier to write and read, though I had my concerns regarding hooks. First off, the state still needs to be stored somewhere and originally it was proposed to be built around this
, which wouldn't work with arrow functions and would need to rely on the fact that JSX is compiled (not the case as at the moment it uses a dispatcher instead). And secondly, it required thinking in React.
While classes were a mouthful - they were straightforward - there were explicit props
and state
, and when state or the props would change - the render
method would be triggered. And there were a couple of methods you could use to control this flow, like shouldComponentUpdate
or componentDidMount
:
class App extends React.Component {
constructor() {
super();
this.state = {
name: ""
};
}
componentDidMount() {
console.log("Component did mount!");
}
shouldComponentUpdate(props, state) {
console.log({
new: { props, state },
old: { props: this.props, state: this.state }
});
return true;
}
render() {
return (
<div>
<h1>Hello, {this.state.name} </h1>
<input
type="text"
value={this.state.name}
onChange={(e) => this.setState({ name: e.target.value })}
/>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("app"));
Which when turned into a succinct functional component with hooks looked liked magic:
const App = () => {
const [name, setName] = React.useState("");
React.useEffect(() => {
console.log("Mounted!");
}, []);
React.useEffect(() => {
console.log("Name changed:", name);
}, [name]);
return (
<div>
<h1>Hello, {name} </h1>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("app"));
I can't say I know every aspect of how it works now, years after, and back when it was some wild juju I gave up trying to comprehend. Unfortunately, things I don't understand have a tendency to bite me when I least expect it.
Handling performance issues
As I mentioned, React was a working great for our dashboard, and so I've decided to switch our plain old MVC website to a fancy server-side rendered React. That was before NextJS became the de-facto standard for this and I've kinda just glued most pieces together myself: after all, it boiled down to replacing the template engine we were using (I think it was pug) with ReactDOMServer:
//
const React = require('react');
const ReactDOMServer = require('react-dom/server');
const script = React.createElement('script',{},'console.log("ReactDOM hydrate would happen here")')
const page = React.createElement('h1',{},'Hello, SSR!');
const app = React.createElement('body',{},page, script);
ReactDOMServer.renderToString(app);
The new version was quite ok and I could add some real reactivity to the otherwise static pages, including the changes to a video player.
I learned that some things required dropping down plain old JS event listeners with the use of refs:
const App = () => {
const videoEl = React.useRef(null);
const [time, setTime] = React.useState(0);
const onTimeUpdate = (e) => {
setTime(e.target.currentTime);
};
React.useEffect(() => {
if (!videoEl.current) return;
videoEl.current.addEventListener("timeupdate", onTimeUpdate);
return () => {
if (!videoEl.current) return;
videoEl.current.removeEventListener("timeupdate", onTimeUpdate);
};
}, [videoEl]);
return (
<div>
<p>{time}s</p>
<video
ref={videoEl}
src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
controls
/>
</div>
);
};
ReactDOM.render(<App />, document.getElementById("app"));
But, as I discovered after, rendering anything on the screen is an expensive task, let alone a high abstraction like HTML/CSS. Now imagine fetching a video stream, processing it, playing and rendering UI changes every frame with the help of a virtual DOM diffing:
Yup, that's what was happening. Now of course, React was not the main issue - the video processing and playing were the heavy ones, but there were so little resources available and so many optimisation required to make it work properly with React, that I gave up and wrote the player interface is plain JavaScript and just "mounted" it on the React component:
const App = () => {
const videoEl = React.useRef(null);
React.useEffect(() => {
if (!videoEl.current) return;
mountVideo(videoEl.current);
return () => unmountVideo(videoEl.current);
}, []);
return (
<div>
<div ref={videoEl} />
</div>
);
};
const mountVideo = (el) => {
el.innerHTML = `<div class='time'>0s</div><video src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" controls/>`;
el.querySelector("video").ontimeupdate = (e) => {
el.querySelector(".time").innerText = `${e.target.currentTime}s`;
};
};
const unmountVideo = (el) => {
el.innerHTML = "";
};
ReactDOM.render(<App />, document.getElementById("app"));
After this I've build several quick prototypes with the help of GraphQL and React Native and worked on yet another dashboard in React / Redux.
I think by then I finally learned to think in React, but nonetheless from time to time I still turn useEffect
into an endless cycle of updates and forget to memoize stuff.
But I didn't want any of it: I didn't want to learn a language within language with a dozen of libraries, I didn't want to change the way I think - I just wanted to make a performant web application as easy as possible. And I couldn't help but resent React for it.
Yet today I came across a really interesting project, called atomico - a WebComponents library, inspired by React hooks; And it dawned upon me that without React, and, particularly JSX - none of it would be possible, not my beloved svelte, not flutter, nor dozens of other frameworks.
And just as I'd advise any web developer to learn basics of so-called "vanilla" JavaScript, I highly recommend to look into a library that shaped the modern web technologies, at least out of curiosity.
Hopefully, it'd take you much less time to master than it took me :-)
Top comments (42)
React did have a major influence on many modern day frameworks. But i do feel many frameworks over complicate simple process and rename it. For e.g. Using a function to generate stateless component...is same as having a function which renders html/template 🙆♂️. It is better to learn vanilla rather than a rebranded version of vanilla feature.
I agree, knowing underlying technology is never a bad idea!
A reason why it would be hard for newbies to just bump in to learning React without knowing the language which inspired it.
The real problem is people learn React(or any other framework), which gradually changes its ways every few years... Later you need to relearn things... All of this because a single entity dictates the way a framework works.... If only most frameworks could lean into vanilla features, their knowledge would be relevant for years to come
I'm still baffled we don't have a component library and DOM diffing being done by shadow DOM on native code in the browser.
Come on, this is 2022.
Thanks for sharing! It was a great read.
Been working with React for some time now and I wouldn't enjoy it as much if I had to work with class components. Functional components actually make sense for me and I think is 100x easier for new people coming to the library. Even for die hard Java programers and their sea of classes.
Thank you!
Yes, it's definitely easier, at least till the first endless cycle 😁
Well... who never have done an endless loop in their programing's journey 😅.
It's definitely gotten better over the years
Probably a large reason why a lot of people are still developers or still work with React. Progress seems to be happening.
This was a great read. Even though I didn't that many shenanigans with React myself. I still saw them happening with several of my friends and colleagues.
Also, it didn't help the fact that I've been in projects that have used React in the wildest of ways (because they were great monoliths very expensive to update, let alone rewrite)
Needless to say, after
React.createClass
thenextends React.Component
and nowuseEffect
and all the possible custom hooks. I don't want anything to do with React anymore.I still keep getting offers for 'React Developer' but I cannot fathom going again to deal with all of its quirks, reading docs to know what I'm supposed to be doing, and spending time debugging or writing tests with yet another framework.
It's was an interesting ride at least. Not a single moment being boring.
Thank you!
Would you mind sharing some wild ways of using React?
I'm really curious now and the worst I can think of is an innocent jsx-powered NodeJS server.
Sure. There was a jsx-powered NodeJS. But there was also the own frontend framework built on top of previous React that had his own server data and a Renderer to take that data an put it into React components. (Who's being used by one of the giants in video streaming service).
There was also the CMS (Drupal) powered, half-static, half-dynamic pages that are being rendered by React but the data comes through a Node server which uses GraphQL to transmit the info from some AWS buckets.
And then there was a project which used Angular 4 to build an entire site for an airplane company, but the catch was... it used micro-frontends with a backend-for-frontend setup.
I don't really which one is worse at this point tbh...
They are wild indeed!
What would you have done differently?
What do you use now?
I spent a good long time on React last year and I found it was a lot of overkill. So I opted for Plain Vanilla JS.
view my article on 17 reasons why at dev.to/rickdelpo1/react-vs-plain-j...
Nice article. 😃 After I had used plain JavaScript for a few years I wanted to try a framework. The first one was React, it was very hard to figure out. But luckily I found Svelte, the best ever framework! It's so much better in almost every way. 😊
My experience with React was like this: I've learned it, I liked it, I used it. Then a new version came around and deprecated everything I've learned, suddenly classes are the 'old way' of doing things. I've realized I won't be able to keep up with the pace of modern frontend changes – I can't relearn the framework every 3 months, not unless I'm a full time frontender. So I've completely dropped web frontend from my skills.
I clicked the link to this post to 'learn more react', I ended up laughing my head off and learning more about 'the micro-evolution of programming' in general. This was an awesome article/post. Best I've read this week.
Thank you very much!
Thanks for sharing!
For me personally, it was very convenient to use django to build small applications in the past, and it also met daily needs. Since I had the opportunity to contact next.js (based on react), many new concepts and understandings made me find it very interesting and refactored. Some small applications, of course I am not a full-time front-end, it is enough for self-use and project tool construction.
I was confused about jsx and javascript for a while in the past, now use jsx, Using jsx saves me a lot of trouble
I feel with you. I never have and probably never will like React. And actively avoid using it. I used Meteor before there was React and I like to write HTML files with and <style> tags ... and such ... so after a short intermezzo with vue.js now svelte is my goto-framework (naturally).</p>
Couldn't agree more especially. First 2 paras about comparison with vanilla js. React looks complicated to begin with..
The odd thing is, it does not need to be. As engineers we have tendance to be to clever, reinvent the wheel and eventually make life hard for ourselves.
I never understood why developers of all levels apply so much state and transformation logic on the frontend. Just because the language allows it and React inadvertently encourages it does not mean it's the right way to go.
For example a junior developer I manage, challenged me when I suggested moving their business logic into an express app from their frontend SPA (personal project). Asking, why would they need to do that as when they can just call the downstream service as is.
I gently replied, well the bundle size is huge, the app takes way to long to figure it self out before a user can view or know what's going on and your API key to your SaaS provider is exposed for anyone to steal and use, and the list went on...
Moving all the heavy lifting into a BFF and just allowing your SPA to be lightweight view will keep things very simple for you to maintain.
That's the deal breaker. Users are only willing to wait a few seconds before deciding to move on (usually to a competitor).
Couldn't agree more, regardless of the framework, it is possible to setup an efficient structure that will be pleasant to work with.