Skip to content

Latest commit

 

History

23 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Notes on React

Project initialization

  1. Run this command
npm create vite@latest
  1. Name your project.

  2. Select a framework: React.

  3. Select a variant: JavaScript + React compiler.

  4. Which linter: ESLint is prefered for small projects.

  5. Run this command to initiate the server:

npm run dev

JSX

Normal pure DOM manipulation requires so much code and hard to read, So JSX is the solution as it allows to writenested HTML tags, but it is not HTML, React converts this JSX into JavaScript and updates the DOM.


Rules of JSX

As we mentioned, JSX is not HTML and has rules that makes it even more powerful

You can't return multiple elements sitting side by side as they need to wrapped in one parent container.

  • This rule comes from JS rule which is you can't return 2 objects from the same function unless you wrap them in an array
  • If you don't like to wrap them in a normal HTML tag, there is a component that React offers which is React.Fragment and can be shortened into <>.

Every tag in JSX must be closed properly, even tags that don't need to be closed in HTML like <br>

  • You can close tags like <br> by adding / before > like <br />.

Attribute names must be written in camelCase

  • HTML attributes sometimes conflict with JS reserved keywords like for.
  • for ===> htmlFor.
  • class ===> className.
  • When this conflict happens, React suggests the correct attribute name in the dev console.

You can embed JS expressions directly into markup using curly braces.

export const Welcome = () => {
    const name = Ahmed Mahmoud;
    const age = 21;

    return (
        <>
            <h1>Welcome, {name}</h1>
            <p>You are born in {new Date().getFullYear() - age}</p>
        </>
    )
}

props

props in JSX are like arguements in JS.

  • In app.jsx
function App() {
    return (
        <>
            <Welcome name="Ahmed" />
        </>
    )
}
  • In Welcome.jsx
export const Welcome = (props) => {
    return (
        <h1>Welcome {props.name}!</h1>
    )
}
  • You can destruct the props object.
  • Strings are passed to the component in double quotes, everything else is in curly braces.

Props patterns

Default props

  • You can use default props, it will be used when the passing values is missing or undefined.
  • If you pass null or 0, it will not be used.
export const Greeting = ({ name = "Ahmed", message = "Hello" }) => {
    return (
        <h1>{message}, {name}!</h1>
    )
}

And that is how JS default params work, this ensures the point of JSX converted at the end to JS.

Forwarding props with spread operator

  • Instead of destructing the props and passing it individually to the component we can do this:
import { Greeting } from './Greeting'

export const UserProfile = ({ id, ...rest }) => {
    return (
        <>
            <Greeting  {...rest} />
            <p>Displaying profile for user {id}...</p>
        </>
    )
}

Passing JSX as children

  • As HTML, we can nest components inside each other.

In ProfileWrapper.jsx

export const ProfileWrapper = ({ title, childeren }) => {
    return (
        <>
            <div>
                <h1>{title}</h1>
                {children}
            </div>
        </>
    )
}

In App.jsx

import { UserProfile } from './UserProfile'
import { ProfileWrapper } from './ProfileWrapper'

function App () {
    return (
        <ProfileWrapper title="Facebook">
            <UserProfile {...info} />
            <button>Post</button>
        </ProfileWrapper>
    )
}

export default App

Conditional rendering

  • If statement: The simplest way, but usually causes a lot of duplication.
  • Ternary operator: Allows to conditionally render parts of the component.
  • AND operator: All or nothing.
<h3>{name} { isPremium && <span> $</span>}</h3>
  • Variables: you can prepare the JSX in variables to avoid messy components.

Rendering lists

If you have a list of items like comments on a post and want to render them, it is not practical to write a div for each comment.

We can instead use map() function to map each comment to it's code.

const productElements = products.map((product) => {
        return (
            <div>
                <h3>{product.name}</h3>
                <p>Price: ${product.price}</p>
            </div>
        )
    })

Yocan even filter them according to a specific condition by filter(), This is the power of JSX.


Keys and how React use them in rendering lists

When React needs to render a list with a new version, it compares it to the old version and see if there any item that needs to be added or omitted to increase efficiency and prevent rendering the whole list again.

React does this by keys.

  • You need to add a key for the list in the outer (parent) tag.
  • The key should be unique:
    • if the data comes from database, use the primary keys of the items.
    • if it is generated in the runtime you might consider uuid package.

So to correct the last code snippet, we need to add a key for React, it can be the id in the product object:

const productElements = products.map((product) => {
        // Now we will add key attribute in the div tag
        return (
            <div key={product.id}>
                <h3>{product.name}</h3>
                <p>Price: ${product.price}</p>
            </div>
        )
    })

Using array index for keys

There is a common approach for keys selection which is array index, this approach is simple and map() function provides index as a second arguement, but before using it you must be sure about those things:

  • There isn't a unique key: There is no need for using index kinstead of a unique key that already exists.
  • List is completely static: You must be sure there are no items being added or deleted.
  • List is never reordered or filtered: This is because of the way React render lists as mentioned in the previous section.

Examples for lists that satisfy those conditions: Navigation menu.


Styling React components

We can style components with the style attribute:

export const Alert = ({ children }) => {
    return (
        <div style={{
            backgroundColor: black;
        }}>{children}</div>
    )
}

The first curly braces for embedding JS object inside JSX, the second one is for the object itself.

This method can get messy very fast if the styling increases, so we separate the styling in a css module, As if we didn't make it a module it will be global.

import styles from './Alert.module.css'

export const Alert = ({ children, type = "success" }) => {
    return (
        <div className={`${styles.alert} ${styles[type]}`}>{children}</div>
    )
}

We wrote styles[type] as type is dynamic class.


Event handling

There is 2 simple steps for responding to events:

  • Define a function that should be executed when the event occurs.
  • Assign this function to a special prop On.

You can also access the event object and extract useful info about the event in the function you defined.

export const CustomButton = () => {
    const handleClick = (e) => { // e is the event object
        alert('Thanks for clicking')
        console.log(e.target)
        console.log(e.button)
    }

    return (
        <button onClick={handleClick}>Click</button>
    )
}

We can also pass the handler function by props if we need to reuse the button for more than one component.

export const MenuItem = ({ name, price, onOrder }) => {
    return (
        // Code
        <button onClick={() => onOrder(name, price)}>Order</button>
    )
}

State

Until now, we still don't update the screen WHY?

  • Changing the values of variables doesn't update the screen.
  • Every time the component renders (the page reloads), all variables reset to their initial values.

To solve this problem, React provides state, it is the component's memory.

Wecan se the useState() hook that React provides, it returns the current state and the setter function and takes in its arguements initial state.

  • Initial state can be:

    • The initial value of the variable we want to update
    • An arrow function, it is called Lazy initialization, React calls this function on component render only and that is when we want to calculate the initial state.
  • Note: If you used the same component that has state twice, updating one will NOT effect the other.

const [count, setCount] = useState(0)

const handleCount = () => {
    setCount(count + 1)
}

About

A collection of React practice projects and notes.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages