React

A JavaScript library for building user interfaces with a UI Components based architecture.

Components

// Function Component
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

// Class Component
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}
  • Function components are simpler and preferred
  • Class components needed for some legacy features

Props

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

<Welcome name="Alice" />
  • Props are read-only
  • Pass data from parent to child components

State

function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
    </div>
  );
}
  • State is mutable and managed within a component

Lifecycle

useEffect(() => {
  // Runs after every render
  console.log('Component updated');
  
  return () => {
    // Cleanup function
    console.log('Component will unmount');
  };
}, [/* dependency array */]);
  • Empty dependency array [] means run once on mount

Conditional Rendering

function Greeting({ isLoggedIn }) {
  return isLoggedIn ? <UserGreeting /> : <GuestGreeting />;
}
  • Use ternary operators or && for simple conditions
  • Extract complex logic into separate functions

Lists and Keys

function NumberList({ numbers }) {
  return (
    <ul>
      {numbers.map((number) =>
        <li key={number.toString()}>{number}</li>
      )}
    </ul>
  );
}
  • Always use keys when rendering lists
  • Keys help React identify changed items

Forms

function NameForm() {
  const [name, setName] = useState('');

  const handleSubmit = (event) => {
    event.preventDefault();
    console.log('Submitted name:', name);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input 
        type="text" 
        value={name}
        onChange={(e) => setName(e.target.value)}
      />
      <button type="submit">Submit</button>
    </form>
  );
}
  • Use controlled components for form inputs
  • Handle form submission with onSubmit event

Context

const ThemeContext = React.createContext('light');

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar() {
  const theme = useContext(ThemeContext);
  return <div>Current theme: {theme}</div>;
}
  • Use context to pass data through component tree
  • Avoid prop drilling for deeply nested components

Hooks

useState

const [state, setState] = useState(initialState);
  • Manages State in function components
  • Returns current state and a function to update it
  • Use when you have simple state logic

useEffect

useEffect(() => {
  // Side effect code
  return () => {
    // Cleanup code (optional)
  };
}, [dependencies]);
  • Handles side effects (e.g., data fetching, subscriptions)
  • Runs after every render by default
  • Use empty dependency array [] to run once on mount
  • Cleanup function runs before component unmounts

useContext

const value = useContext(MyContext);
  • Consumes value from nearest Context.Provider
  • Allows accessing context without nesting
  • Re-renders when context value changes

useReducer

const [state, dispatch] = useReducer(reducer, initialState);
  • Alternative to useState for complex state logic
  • Uses a reducer function to determine state updates
  • Helpful for managing related state variables

useCallback

const memoizedCallback = useCallback(() => {
  doSomething(a, b);
}, [a, b]);
  • Returns a memoized version of the callback
  • Useful for optimizing child component re-renders
  • Only changes if one of the dependencies has changed

useMemo

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
  • Returns a memoized value
  • Recalculates only when dependencies change
  • Use for expensive calculations to avoid unnecessary re-computation

useRef

const refContainer = useRef(initialValue);
  • Returns a mutable ref object
  • Persists across re-renders
  • Commonly used to access DOM elements
  • Can store any mutable value without causing re-renders

Custom Hooks

function useCustomHook() {
  // Hook logic here
  return /* relevant values */;
}
  • Allows you to extract component logic into reusable functions
  • Must start with "use" to follow Hook naming convention
  • Can call other Hooks inside custom Hooks

Server Components

async function ServerComponent() {
  const data = await fetchDataFromDatabase();
  return <div>{data}</div>;
}
import ServerComponent from './server-component';

export default function Page() {
  return (
    <div>
      <h1>My Page</h1>
      <ServerComponent />
    </div>
  );
}
  • Introduced in React 18
  • Run on the server, not in the browser
  • Can access server resources directly (e.g., databases)
  • Reduce bundle size by keeping heavy logic on server
  • Don't have state or lifecycle methods
  • Can't use hooks or handle browser events
  • Seamlessly integrate with client components

Key benefits

  • Improved performance
  • Reduced client-side JavaScript
  • Better SEO
  • Easier data fetching

Usage notes

  • Use for components that don't need interactivity
  • Combine with client components for dynamic UIs
  • Requires a compatible framework (e.g., Next.js)

Best Practices

  • Keep components small and focused
  • Lift state up when needed
  • Use composition over inheritance
  • Avoid premature optimization
  • Write tests for your components