In this article, I will discuss very basic React fundamentals. Knowing basic javascript is good enough to follow along with the article. Even though you have been working with React for some time, you might learn some new things or justifications for certain things that you already know from this article.
This article is HEAVILY based on React Fundamentals workshop by Kent C Dodds which will also be a part of Epic React course soon to be released. If you are more like a
learning by doing
type of person similar to me, head over to the React Fundamentals repo and follow the instructions in the README. This article is more or less a compilation of things in that repo with my own explanations. I will add some more things to this article in the future based on feedback.
Table of Contents
- Basic JavaScript-rendered Hello World
- Intro to raw React APIs
- Using JSX
- Creating custom components
- Styling
- Forms
Basic JavaScript-rendered Hello World
Let's see how to render Hello World using basic javascript.
My HTML contains a single div
with id
as root
<div id='root'></div>
Now, I want to add <div class='container'>Hello World</div>
to that root div.
We can do that using javascript's document API. Let's see how
// Fetching the root div element
const rootElement = document.getElementById('root')
// Creating a new div as per our requirements
const divElement = document.createElement('div')
divElement.textContent = 'Hello World'
divElement.className = 'container'
// Appending newly created div element to the root element.
rootElement.append(divElement)
What we are doing here is very simple.
- Get a reference to the actual DOM root element
- Create a new div element using
document.createElement
and then set its class and textContent - Append this newly created element to the root div element.
This produces the following HTML markup.
<div id='root'>
<div class='container'>Hello World</div>
</div>
Intro to raw React APIs
Now let's try to use React's raw APIs to create markup that we need instead of (vanilla) javascript.
We need two important APIs to achieve our task. In vanilla javascript. they are:
document.createElement()
rootElement.append(domElement)
The React's equivalent of these two APIs are:
React.createElement()
ReactDOM.render(reactElement, rootElement)
Let's see React.createElement()
in more detail.
React.createElement()
This accepts three parameters
- Component or Tag to use to create the element
- Props for the component
- Children
The API looks like React.createElement(component, props, ...children)
So, to create an element like <div class='container'>Hello World</div>
, you would do
React.createElement('div', { className: 'container' }, 'Hello World')
Our HTML will have
<div id="root"></div>
Now, to append <div class='container'>Hello World</div>
to the root element using React, we do:
const rootElement = document.getElementById('root')
const divElement = React.createElement('div', {className: 'container'}, 'Hello World')
ReactDOM.render(divElement, rootElement)
Can you see how similar the React's API is to the normal vanilla js document API?
Note that, you can even create nested elements using this.
For example, let's try to create the following markup.
<div class='container'>
<span>Hello</span>
<span>World</span>
</div>
To create the above markup
const rootElement = document.getElementById('root')
const helloElement = React.createElement('span', null, 'Hello')
const worldElement = React.createElement('span', null, 'World')
const divElement = React.createElement('div', {className: 'container'}, helloElement, worldElement)
ReactDOM.render(divElement, rootElement)
You can even use a special children
prop to add the children like the following
React.createElement('div', {className: 'container', children: [helloElement, worldElement]})
The above code is same as the below code
React.createElement('div', {className: 'container'}, helloElement, worldElement)
Using JSX
If you have already been using React or saw the React code any time, you most probably would not have seen React.createElement
being used. Instead, you might have seen some code that looks similar to HTML. Let's see what it is.
JSX is HTML-like syntactic sugar on top of raw React APIs.
Let's see an example.
const divElement = <div id='container'>Hello World</div>
The above code is equivalent to
const divElement = React.createElement('div', {id: 'container'}, 'Hello World')
But JSX is not a valid javascript code, so we use a compiler called Babel
to convert JSX code to its corresponding React.createElement
code.
Now, let's see how easy it is to create the following markup using JSX.
<div class='container'>
<span>Hello</span>
<span>World</span>
</div>
const rootElement = document.getElementById('root')
const divElement = <div className='container'><span>Hello</span><span>World</span></div>
ReactDOM.render(divElement, rootElement)
Note that there are some subtle differences between JSX and HTML.
For example, in HTML to add class to an element, we add it like class='container'
, whereas in JSX, we need to write className='container'
.
There are some other differences when using JSX which I will explain later in the post.
Interpolation in JSX
Since JSX is written in javascript itself, there are some very interesting things which you can do. One of those is using JSX interpolation. It basically gives us the ability to use javascript inside of JSX. Whenever you do interpolation, you would surround it with {
and }
. This tells the Babel compiler that interpolation is being used here.
For example, take the following code
const divElement = <div className='container'>Hello World</div>
Now, you want the class name and text content to be dynamic, you can do something like
const divClassName = 'container'
const divTextContent = 'Hello World'
const divElement = <div className={divClassName}>{divTextContent}</div>
Can you see the flexibility that interpolation gives us?
Conditionals and Loops
You can even add conditionals and loops in JSX
{ condition ? <div>Hello World</div> : <div>Goodbye World</div> }
As you can see above, to use conditionals in JSX, you would make use of the tertiary operator.
{items.map((item) => <div key={item.id}>{item.title}</div>)}
To use loops, you will use the map
function.
You can even use template literals in JSX like
const element = <div id={`item-${itemId}`}>{itemContent}</div>
To learn more about JSX, head over to JSX in Depth - React's official docs
Creating custom components
Consider the following JSX code
<div className='container'>
<div className='message'>Hello World</div>
<div className='message'>Goodbye World</div>
</div>
Here you can see the code <div className='message'></div>
is duplicated at two places.
To avoid duplication, the simplest thing we can do is to create a function and then call it instead.
function message(text) {
return <div className='message'>{text}</div>
}
<div className='container'>
{message('Hello World')}
{message('Goodbye World')}
</div>
Let's refactor this a bit.
function message({children}) {
return <div className='message'>{children}</div>
}
<div className='container'>
{message({children: 'Hello World'})}
{message({children: 'Goodbye World'})}
</div>
Let's refactor this even more to use React.createElement
function message({children}) {
return <div className='message'>{children}</div>
}
<div className='container'>
{React.createElement(message, null, 'Hello World')}
{React.createElement(message, null, 'Goodbye World')}
</div>
Previously in all the examples that we have seen, the first argument of React.createElement() is a string like 'span' or 'div'.
But React.createElement also accepts a function that returns something renderable like JSX, some string, number etc.
That's why the above code works.
Now, let's convert the above code to JSX
function message({children}) {
return <div className='message'>{children}</div>
}
<div className='container'>
<message>Hello World</message>
<message>Goodbye World</message>
</div>
The above code seems to be perfect, right? Actually it's not. The above code will not work as intended. The reason being how babel compiles the JSX code to it's corresponding React.createElement() code.
<message />
is compiled by babel to React.createElement('message')
. But what we want is React.createElement(message)
. In the first case, the first argument is a string, in the second case, it's a function.
For the babel to convert it into what we needed, we have to make the message
function name uppercase.
function Message({children}) {
return <div className='message'>{children}</div>
}
<div className='container'>
<Message>Hello World</Message>
<Message>Goodbye World</Message>
</div>
Now, this <Message>Hello World</Message>
will be compiled to React.createElement(Message, {children: 'Hello World'})
, which is exactly what we needed.
Check the below examples to see how Babel compiles each of the JSX formats.
JSX | React.createElement() |
---|---|
<Capitalized /> |
React.createElement(Capitalized) |
<property.access /> |
React.createElement(property.access) |
<Property.Access /> |
React.createElement(Property.Access) |
<Property['Access'] /> |
SyntaxError |
<lowercase /> |
React.createElement('lowercase') |
<kebab-case /> |
React.createElement('kebab-case') |
<Upper-Kebab-Case /> |
React.createElement('Upper-Kebab-Case') |
<Upper_Snake_Case /> |
React.createElement(Upper_Snake_Case) |
<lower_snake_case /> |
React.createElement('lower_snake_case') |
So, we can see that the component name needs to be UpperCamelCased for it to work as intended.
PropTypes
Let's slightly change the previous message component so that it accepts name
prop.
function Message({name}) {
return <div className='message'>Hi, your name is {name}.</div>
}
<Message name='foo' />
<Message />
This produces
Hi, your name is foo.
Hi, your name is .
This doesn't look good. Does it? So what if there is a way we can enforce that the name needs to be passed and it needs to be a string.
Luckily, React gives us a way to do that using PropTypes
.
Let's create a PropType
to enforce the type of name
to be string
.
const PropTypes = {
string(props, propName, componentName) {
if (typeof props[propName] !== 'string') {
return new Error(`In component ${componentName}, ${propName} needs to be a string, but it was of type ${typeof props[propName]}`)
}
},
}
function Message({name}) {
return <div className='message'>Hi, your name is {name}.</div>
}
Message.propTypes = {
name: PropTypes.string,
}
Now every time you pass anything other than string
for name
prop, it throws an error.
Since cases like these are so common, the React
team developed a library called prop-types which you can use in a similar way - PropTypes.string.isRequired
. Check out the repo for more details.
Note that PropTypes will be disabled in a production environment for performance reasons.
React Fragments
<div id='root'></div>
Let's consider the following use case.
You have to add <span>Hello</span>
and <span>World</span>
to the rootElement
using React.
In the end, the markup should look like
<div id='root'>
<span>Hello</span>
<span>World</span>
</div>
Let's see if we can do this.
const rootElement = document.getElementById('root')
const elementOne = React.createElement('span', null, 'Hello')
const elementTwo = React.createElement('span', null, 'World')
ReactDOM.render(?????, rootElement)
Now, what should be in the place of ?????
in the last line. It can neither be elementOne
nor elementTwo
, because we want both of them to be rendered (not one). But ReactDOM.render()
takes only one react element as an argument and then appends it to rootElement.
One way to achieve this is if we can wrap both of the elements in a new element.
const rootElement = document.getElementById('root')
const elementOne = React.createElement('span', null, 'Hello')
const elementTwo = React.createElement('span', null, 'World')
const combinedElement = React.createElement('div', null, elementOne, elementTwo)
ReactDOM.render(combinedElement, rootElement)
The above code may look fine, but it produces different HTML than what we needed.
<div id='root'>
<div>
<span>Hello</span>
<span>World</span>
</div>
</div>
This is the reason why you can't do something like the following in your code.
function Message() {
return <span>Hello</span><span>World</span>
}
Because there is no way for babel to be able to convert this to a single React.createElement()
React Fragments are introduced in React v16.2.0
exactly to solve this problem. Now you can return multiple elements by just wrapping them around with React.Fragment
.
For example,
function Message() {
return (
<React.Fragment>
<span>Hello</span>
<span>World</span>
</React.Fragment>
)
}
React will ignore this React.Fragment
when rendering.
So the previous problem can be solved now in the following way.
const elementOne = React.createElement('span', null, 'Hello')
const elementTwo = React.createElement('span', null, 'World')
const combinedElement = React.createElement(React.Fragment, null, elementOne, elementTwo)
ReactDOM.render(combinedElement, rootElement)
There is a short hand representation for React.Fragment
.
Instead of writing <React.Fragment>{childrent}</React.Fragment>
, you can write something like <>{children}</>
. Both yield absolutely same result.
Styling
There are two general ways to style React components.
- Inline CSS
- Regular CSS
Let's see inline CSS first
Inline CSS
In normal HTML too, you can add inline styles to your HTML elements by adding your styles as a string to style
attribute.
<div style="color: red; font-style: italic;">Red Italic Text</div>
In React
also you would add your styles to style
prop, but instead of a string
, style
prop accepts an object
.
For example,
const elementStyle = {
color: 'red',
fontStyle: 'italic'
}
<div style={elementStyle}>Red Italic Text</div>
You can even inline elementStyle
if you like
<div style={{ color: 'red', fontStyle: 'italic' }}>
Red Italic Text
</div>
Another difference with styles in React from that of HTML is property names needs to be camelCased
instead of kebab-cased
. For example, in React styles, background-color
will become backgroundColor
, font-style
will become fontStyle
, etc.
Also the value of the style property is always string
or number
(since style
needs to be a proper javascript object, things like #fff
or 20px
are not proper javascript values). So you cannot write something like fontSize: 20px
, instead you need to write fontSize: '20px'
. Similarly you cannot write color: #fff
, you need to write color: '#fff'
.
Regular CSS
Using regular css is straight forward. You just add the classNames and ids that you need and then style your elements using those accrodingly.
Forms
Consider the following form
<form>
<div>
<label htmlFor="usernameId">Username:</label>
<input id="usernameId" type="text" name="username" />
</div>
<button type="submit">Submit</button>
</form>
Now handling forms in React is very similar to how we do in normal javascript. You just define a submit handler and then assign it to the onSubmit event of the form.
function handleSubmit(event) {
event.preventDefault()
// You can get the value of username in one of the following ways.
// event.target.elements[0].value
// event.target.elements.usernameId.value
// event.target.elements.username.value
// Do whatever you want with the username
}
Using Refs
There is another way to get the reference to an element in React - using Refs.
Refs are special objects in react that stays consistent between rerenders of the component and also changing it will not cause the component to rerender.
You can create a Ref using React.useRef()
const myRef = React.useRef()
Refs will have a current
property which contains the value of ref. If you assign a ref
to a React element, ref.current
will automatically have the reference to the object.
For example
<input ref={myRef} />
Now myRef.current
will have reference to that input element.
Let's make use of ref to get the username in our form.
function UsernameForm() {
const usernameInputRef = React.useRef()
function handleSubmit(event) {
event.preventDefault()
// usernameInputRef.current.value will have the value of the input
}
return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="usernameInput">Username:</label>
<input id="usernameInput" type="text" ref={usernameInputRef} />
</div>
<button type="submit">Submit</button>
</form>
)
}
Go through useRef - official docs to learn more about refs.
There is more to learn about React Forms. Go through official docs to learn more.
This article is HEAVILY based on React Fundamentals workshop by Kent C Dodds which will also be a part of Epic React course soon to be released. If you are more like a
learning by doing
type of person similar to me, head over to the React Fundamentals repo and follow the instructions in the README. This article is more or less a compilation of things in that repo with my own explanations. I will add some more things to this article in the future based on the feedback that I receive.
Did you learn anything new from this article? Tell me in the comments.
Links and References
- React Fundamental Workshop by Kent C Dodds
- Epic React
- Beginner's Guide to React
- React Documentation
- React Without JSX
- Introducing JSX
- JSX in Depth
- Typechecking with PropTypes
- React Fragments
- Inline Styles
- Styling and CSS
- Forms
- useRef
Other articles of mine that you might like
- Create Your Own Super Simple URL Shortener
- Why you should start using HSL color format
- Hyphenate when you justify text
- Embed Youtube Videos into Your Markdown Editor
- Babel Plugin To Remove Console Logs In Production
- noopener, noreferrer, and nofollow - When to use them? How can these prevent Phishing Attacks?
- Create New CodePens and CodeSandboxes Instantly With ZERO clicks
- How to Import SVGs into your Next.js Project?
- Create Custom Markdown Parser
- Optimize Your Website For Social Media Platforms
- Add Typescript to your Next.js project
Top comments (0)