React fundamentals and pitfalls

React fundamentals and pitfalls. Mostly from the official doc.

  • React components are regular JavaScript functions whose names must always start with a capital letter
function MyButton() {
  return (
    <button>I'm a button</button>
  );
}
  • The markups must be wrapped in a pair of parentheses if they aren't all on the same line as the return keyword
return (
  <div>
    <img src="https://i.imgur.com/MK3eW3As.jpg" alt="Katherine Johnson" />
  </div>
);
  • JSX is stricter than HTML and tags must always be closed
<img className="avatar" />
<br />
<MyButton />
<h1>This is heading</h1>
<button>I'm a button</button>
//...
  • React component can’t return multiple JSX tags which must be wrapped in a shared parent, like a <div>...</div> or an empty <>...</> wrapper
function AboutPage() {
  return (
    <>
      <h1>About</h1>
      <p>Hello there.<br />How do you do?</p>
    </>
  );
}
  • Components can render other components but never nest their definitions
export default function Gallery() {
  // 🔴 Never define a component inside another component!
  function Profile() {
    // ...
  }
  // ...
}
  • CSS class is specified with className and inline style properties must be written in camelCase
<img className="avatar" />

<ul style="background-color: black"> // html
<ul style={{ backgroundColor: 'black' }}> // react
  • Curly braces are used to escape back into JavaScript for using JavaScript variables or expressions
const user = {
    name: 'Hedy Lamarr',
    imageUrl: 'https://i.imgur.com/yXOvdOSs.jpg',
    imageSize: 90,
};

export default function Profile() {
    return (
        <>
            <h1>{user.name}</h1>
            <img
                className="avatar"
                src={user.imageUrl}
                alt={'Photo of ' + user.name}
                style={{
                    width: user.imageSize,
                    height: user.imageSize
                }}
            />
        </>
    );
}

function MyButton() {
    function handleClick() {
        alert('You clicked me!');
    }

    return (
        <button onClick={handleClick}>
            Click me
        </button>
    );
}
  • Curly braces must follow = immediately without " when used for attributes and not used as dynamic variable template
// src={avatar}  OK
// src="{avatar}"  NG

// <h1>{name}'s To Do List</h1>  OK
// <{tag}>Gregorio Y. Zara's To Do List</{tag}>  NG
  • Double curly braces are needed to pass objects
export default function TodoList() {
    return (
        <ul style={{
            backgroundColor: 'black',
            color: 'pink'
        }}>
            <li>Improve the videophone</li>
            <li>Prepare aeronautics lectures</li>
            <li>Work on the alcohol-fuelled engine</li>
        </ul>
    );
}
  • Props can be forwarded to children with spread syntax
function Profile(props) {
  return (
    <div className="card">
      <Avatar {...props} />
    </div>
  );
}
  • Props are immutable and children components have to ask parent component to pass new values via set state
function Clock({ color, time }) {
  return (
    <h1 style={{ color: color }}>
      {time}
    </h1>
  );
}
  • Parent component has a children prop set to the content nested inside
<Card>
  <Avatar />
</Card>

function Card({ children }) {
    return (
        <div className="card">
            {children}
        </div>
    );
}

function Avatar({ person, size }) {
    return (
        <img
            className="avatar"
            src={getImageUrl(person)}
            alt={person.name}
            width={size}
            height={size}
        />
    );
}
  • Conditional rendering works in different ways
// with if/else condition
let content;
if (isLoggedIn) {
  content = <AdminPanel />;
} else {
  content = <LoginForm />;
}
return (
  <div>
    {content}
  </div>
);

// with ternary operator
<div>
    {isLoggedIn ? (
        <AdminPanel />
    ) : (
        <LoginForm />
    )}
</div>

// with shortcut operation
<div>
    {isLoggedIn && <AdminPanel />}
</div>
  • Rendering lists with for loop or map method and each item must have a unique immutable key
const products = [
  { title: 'Cabbage', id: 1 },
  { title: 'Garlic', id: 2 },
  { title: 'Apple', id: 3 },
];

const listItems = products.map(product =>
    <li key={product.id}>
        {product.title}
    </li>
);

return (
    <ul>{listItems}</ul>
);
  • Hooks must be called at the top level of components or other hooks
function MyButton() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <button onClick={handleClick}>
      Clicked {count} times
    </button>
  );
}
  • Use updater function to update some state multiple times in one event
function Counter() {
  const [number, setNumber] = useState(0);

  return (
    <>
      <h1>{number}</h1>
      <button onClick={() => {
        setNumber(n => n + 1);
        setNumber(n => n + 1);
        setNumber(n => n + 1);
      }}>+3</button>
    </>
  )
}
  • Use the spread syntax to create copies of objects or arrays for setting state instead of mutating in place
setPerson({
  ...person, // Copy other fields
  artwork: { // but replace the artwork
    ...person.artwork, // with the same one
    city: 'New Delhi' // but in New Delhi!
  }
});

setArtists([
    { id: nextId++, name: name },
    ...artists // Put old items at the end
]);

react javascript