Working with Components Props and JSX - vonschappler/Ultimate-React GitHub Wiki

Rendering Root and Strict Mode

The basic code to create and render any React application in scrict mode is displayed below. Note that React Strict Mode is used as developer tool for hightliing bugs and issues inside the React application codebase, provinding warnings as feedbacks for errors, without affecting the result, because will not render any visible UI.

import React from 'react';
import ReactDOM from 'react-dom/client';

function App() {
  return <h1>Hello React!</h1>;
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

Debugging React code

Steps for debugging:

  1. Make sure the application is running by using npm start
  2. Stop the app (Ctrl + C) and restart it with npm start
  3. Hard reload the brower
  4. Keep both terminal and developer tools opened in the browser to check for errors
  5. Try googling for the error messages you got, if any error messages are displayed either on the console, developer tools or as an overlay in the browser where the application should be renderdered
  6. Always keep ESLint running - it will either warn you or show an error as a way to prevent those happen
  7. Remember to check both the Problems and Output tabs on VSCode terminal for errors and warnings
  8. Try comparing the code with the codes provided with the classes

React components

React applications are entriely made of components, which are building blocks of an User Interface (UI). In technical terms, React renders components all together into that UI.

Each component has its own data/state, logic and appearance, which defines how it works and should look like.

So, it's reasonably to say that we "build complex UIs by creating multiple components and combining them if they were lego pieces".

In React, components can be (and are) resued and/or nested inside each other, passing data between each other, using something called props.

Components trees always help on how to break down UIs into components, helping in understanding their relationship (parent and child components) and how they interact with each other.


Creating and Reusing components

In React, components are functions. Thise functions are defined as components when they:

  • Have their name started with a capital Letter
  • Return some markup language (mostly in HTML format)
  • Returns a single element defined by the markup language
  • The component is declared in the top level of the code

Below there is an example of a simple component:

function Title() {
  return <h1>This is my title!</h1>;
}

As mentioned components can be reused. The example below show how a component can be reused inside a React application:

function Title() {
  return <h1>This is my title!</h1>;
}

function App() {
  return (
    <div>
      <Title />
      <Title />
      <Title />
    </div>
  );
}

What is JSX?

JSX is a declerative syntax to describe what components look like and how they work. Each component must return a block of JSX.

This syntax allow devs to combine embed JavaScript, CSS, and other React components into a single block of HTML code.

The HTML code inside the JSX syntax gets converted into a React.createElement function call by a tool caled Babel which is added whenever a new project is created with the command npm create-react-app <project_name>.

Diferences between declarative and imperative programming

  • Imperative (describes step-by-step what we want to see and how to do it):
    • Requires manually DOM element selection, manipulation and transversing
    • Each mutation to the DOM needs to be done step-by-step in the code
  • Declarative (describes what we want to see, without saying how to do it)
    • Descibes how the DOM should look like using JSX, based on the current data (props and states) without any DOM manual manipulation
    • React is then an abstraction away from the DOM, since developers don't need to "touch" the DOM
    • The UI is then a "reflection of the current data" passed to it via the props and states

JavaScript logic in components

Because Components are written as JavaScript functions, it's possible to add any logic inside them. The code below shows a simple example of this.

function Title() {
  const hour = new Date().getHours();
  const isMorning =
    hour <= 11 ? `It's ${hour}h in the morning now!` : `It's not morning.`;
  return <h1>{isMorning}</h1>;
}

Separation of Concerns

At the rise of Interactive SPAs (single page application):

  • Traditional separation of concerns - one technology per file
  • The JavaScript is even more in charge of HTML
  • Logic and UI are tightly coupled

So... "why keep files separated?" this then the why React and JSX get into place. JSX comes into action to colocate logic and UI that change together into a "single place". This colocation of logic and UI is the fundamental reason that defines the existence of components.

React then:

  • Instead of having one tecnology per file, we have one component per file, which defines the new separation of concerns
  • These "concerns" have all tecnology necessary together
  • Keeps still logic and UI coupled, but in a different way

Styling components on React

React "does not care" on how any component is styled. This means that styles can be applied to component in many ways:

  • Inline styling Inline styling using the JSX syntax is a bit different than the usual inline styling we are used on normal HTML files, with any CSS properties added as JavaScript code inside an object.

This object must containt a set of "key": "values" inside it, where the key makes reference to the CCS property (always entered into camel-case) and the value always as a string. The code below shows a quick example of how to it:

function Title() {
  const styles = {
    color: 'red',
    fontSize: '48px',
    textTransform: 'uppercase',
    textAlign: 'center',
  };
  return <h1 style={styles}>My title</h1>;
}
  • External CSS files To add external CSS to a react component, we need first to import that file into the application / component file.

After that, inside the component defined, we add a property/attribute called className and set it's value to the class inside the imported CSS file. The code below shows how this structure should be created:

// react imports here...
import 'path/to/css/file';

function Title() {
  return <h1 className='class-inside-css'>My Title</h1>;
}

function App() {
  <Title />;
}
  • Tailwind css
  • Sass css

Passing and Receiving props

Props can be defined as communication between parent and child components. This is the way of passing data betwen those components being part of one of the fundamentals of React.

This is the way React uses to customize the components based on the data/state provided of each component.

This is done into two steps:

  1. Each prop is defined as an attribute of the component in the parent element
  2. In the component we receive those attributes as the function parameter and make use of the inside JavaScript blocks

The code below shows how to use props in React:

function Title(props) {
  return <h1>{props.text}</h1>;
}

function App() {
  <Title text='Hello world!' />;
}

Props, Immutability and One-Way data flow

Props are used to pass data from parent components to child components. They are an essential tool use for customization and configuration of components. Anything accecpted by JavaScript code can be passed as a prop: values, arrays, strings, objects, functions and even other components.

Thanks to those props, the parent component controls how the child components will look and work.

Keep in mind that even though props control can the look and feel of a component, they are not the "only kind" of data a component can use. Think as if the data passed to a component is divided into small pieces of data, being two of them the Props and State.

The State is the part of data which can be updated by the internal logic of a component itself, while the Props is the part of data coming from the parent component, being updated only by the parent component, because it comes from outside the component.

So, as a React (strict) rule, we take that Pros are immutable, being read-only. If we whish to make use of mutable props, they must be defined as a State.

Props are immutable to prevent mutation on the parent component, causing possible side effects on the application. Remember that in JavaScript, when you create a copy of an object and mutate it, the original version of it is also mutated.

Components in React have to be "pure functions" in termos of props and state, to prevent those side effects. Keeping components that way, React will optmize the application, preventing bugs and making it predictable.

One-Way data flow is one of the principles of React, meaning that data only flows from parent components to child components, not the other way.

The reasons for this:

  • Make applications predictable and easier to understand
  • Meke application easier to debug, as we know exactly where the data is coming from
  • Makes the application perform a lot better than in a two-way data flow, used by frameworks such as Angular, for example

Rules of JSX

  • General rules:
    • JSX works similar to HTML, with a possibility of adding JavaScript to it, using {} (mostly on text and attributes)
    • Inside {}, it's possible do enter any expression such as variable referencing, object/array creation, ternary operations
    • Statements like if/else, for, switch are not allowed
    • JSX always produces a JavaScript Expression - this make insertion of other JSX code inside {} as well as it makes it possoble to add any JSX code inside a component
    • Each piece of JSX can have only a one root element - to make use of more root elements, make use of <React.Fragment> or <>

Rendering lists

Rendering a list is on it's simplest definition the rendering of components based on data provided in the form of an array, allowing to insert them dynamically on the application.

This process can be achieved by using the map method, as displayed below. It's important to noticed though, that whenever rendering components using this method, a key parameter to uniquely identify the component (mostly used for React optmizations is required to be passed as a prop)

const names = ['Maria', 'Joseph', 'Anthony'];

function Greeting(props) {
  return <h1>Hello {props.name}!</h1>;
}

function Greetings() {
  return names.map((el) => {
    return <Greeting name={el} key={el} />;
  });
}

Conditional Rendering

Coditional rendering is all about telling React how and if a component or JSX will be rendered based on a state/prop. This can be achived in three diferent ways:

  • Using the && operator (renders nothing due to short-circuiting)
function TimeOfDay() {
  const hour = new Date().getHours();
  const dayTime = 6;
  const nightTime = 18;
  const isMorning = hour >= dayTime && hour <= nightTime;
  return <div>{isMorning && <p>The sun is up!</p>}</div>;
}
  • Using ternaries (allows providing an alternative rendering)
function TimeOfDay() {
  const hour = new Date().getHours();
  const dayTime = 6;
  const nightTime = 18;
  const isMorning = hour >= dayTime && hour <= nightTime;
  return (
    <div>{isMorning ? <p>The sun is up!</p> : <p>It's nighttime!</p>} </div>
  );
}
  • Multiple returns - (adding if clause outside the JSX returned by the component, mostly used to render or not an entire component)
function MyMessage(props) {
  const toRender = props.msg;
  if (toRender === 'day') return <p>It's daytime!</p>;
  if (toRender === 'night') return <p>It's nightime!</p>;
}

function TimeOfDay() {
  const hour = new Date().getHours();
  const dayTime = 6;
  const nightTime = 18;
  const isMorning = hour >= dayTime && hour <= nightTime;
  if (!isMorning) {
    return <MyMessage msg='night' />;
  }
  return (
    <div>
      <MyMessage msg='day' />
    </div>
  );
}

Recomendations:

  1. Use the ternary operator when trying to render "pieces" of JSX based on conditions
  2. Use the multiple returns when trying to render or not an entire component based on conditions or even rendering a totally different component - this technique is also known as "early return"

Extracting JSX into new components

Extracting JSX into new components helps to keep the code cleaner and it's really useful when a certain component starts getting too big.

If the extracted component depends on states / pieces of data, the functional created need to receive its attributes (props), as already discussed in this session.


Destructuring Props

To destructure a prop, all that is required is to pass over the prop name passed to a component as a destructured object to the function that defines the component. The code below is a small example of how to do that.

// By doing this, it's easy to know that this component expects a prop defined as msg in order to be rendered correctly
function MyMessage({ msg }) {
  if (msg === 'day') return <p>It's daytime!</p>;
  if (msg === 'night') return <p>It's nightime!</p>;
}

function TimeOfDay() {
  const hour = new Date().getHours();
  const dayTime = 6;
  const nightTime = 18;
  const isMorning = hour >= dayTime && hour <= nightTime;
  if (!isMorning) {
    return <MyMessage msg='night' />;
  }
  return (
    <div>
      <MyMessage msg='day' />
    </div>
  );
}

React Fragments

React framents are used when we wish to render two or more components separatedly, by grouping them into a "virtual div".

This is achived easly by adding <></> or <React.Fragment></React.Fragment> as a wrapper around the components to be rendered. The code below exemplifies the use of this:

function TimeOfDay() {
  const hour = new Date().getHours();
  const dayTime = 6;
  const nightTime = 18;
  const isMorning = hour >= dayTime && hour <= nightTime;
  if (!isMorning) {
    return (
      <React.Fragment key='any-key'>
        <MyMessage msg='night' />
        <p>Time to sleep! Go get some rest!</p>
      </React.Fragment>
    );
  }
  return (
    <>
      <MyMessage msg='day' />
      <p>Wakey, wakey!!!!!</p>
    </>
  );
}

As a general convention, <React.Fragment> is mostly used when rendering lists of fragments, because lists, as already discussed, need unique keys for each list item so React when better optmize the code. With fragments lists, the only way to pass the unique key is by creating the fragments with the <React.Fragment key='key-unique-value'> instead of using simply <> as wrapper.


Setting Classes and Text Conditionally

The best way to set Classes and text condittionaly is by making use of the ternary operator.

The example below is a demonstration of how to do it:

function Car({ carObj }) {
  return (
    <li className={`car-item ${carObj.soldOut && 'sold-out'}`}>
      <img src={carObj.photoName} alt={carObj.name} />
      <div>
        <h3>{carObj.model}</h3>
        <p>{carObj.year}</p>
        <span>{!carObj.soldOut ? `${carObj.price}` : `SOLD`}</span>
      </div>
    </li>
  );
}
⚠️ **GitHub.com Fallback** ⚠️