Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Core React Development: Project Setup, Hooks, State Management, and Routing

Tech Jul 15 2

Bootstrapping a React Project

Install the latest project scaffolding tool globally or use npx for a temporary install:

npm install -g create-react-app
npx create-react-app my-app

For TypeScript support, append --template typescript.

Component Basics

Components are the building blocks. Use ES7 snippets in VS Code to generate class (rcc) or functional (rfc) skeletons.

Class component:

import React, { Component } from 'react';

export default class Welcome extends Component {
  render() {
    return <div>Welcome to React</div>;
  }
}

Functional component:

import React from 'react';

export default function Greeting() {
  return <div>Hello, Functional Component</div>;
}

Key rules: component names start with uppercase; render() is mandatory in class components; return a single root JSX element; wrap multiline returns in parentheses.

Composition and Nesting

Components can be nested freely:

class Sidebar extends Component {
  render() {
    return (
      <div>
        Sidebar
        <Child />
      </div>
    );
  }
}

function Banner() {
  return <div>Banner</div>;
}

export default class App extends Component {
  render() {
    return (
      <div>
        <Sidebar />
        <Banner />
      </div>
    );
  }
}

Styling Approaches

Use inline styles or external CSS files. In JSX, class becomes className. Dynamic styles leverage JavaScript expressions inside {}:

const items = [
  { title: 'Carrot', type: 'vegetable', id: 1 },
  { title: 'Apple', type: 'fruit', id: 2 },
];

export default function ShoppingList() {
  return (
    <ul>
      {items.map(item => (
        <li
          key={item.id}
          style={{ color: item.type === 'fruit' ? 'orange' : 'green' }}
        >
          {item.title}
        </li>
      ))}
    </ul>
  );
}

Event Handling

React uses synthetic events. Attach handlers via props like onClick, onMouseOver. In class components, prefer arrow function bindings to preserve this:

<button onClick={() => this.handleDelete(index)}>Delete</button>

Event delegation is used internally – a single listener at the root.

Importing and Exporting Modules

Default exports: one per file.
Named exports: multiple per file.

// ChildComponent.js
export function Avatar() { ... }
export default function Profile() { ... }

// Parent.js
import Profile from './ChildComponent';
import { Avatar } from './ChildComponent';

Working with Refs (Class Components)

Create a ref to access DOM nodes:

inputRef = React.createRef();
// Usage: <input ref={this.inputRef} />
// Read value: this.inputRef.current.value

State Management in Class Components

State is initialized as a class property:

state = { count: 0 };

Update with setState():

this.setState({ count: this.state.count + 1 });

setState is asynchronous in synchronous code and merges updates. Use a callback to access the latest state.

ToDo list example:

export default class TaskList extends Component {
  textInput = React.createRef();
  state = { tasks: [{ id: 1, text: 'Learn React' }] };

  addTask = () => {
    const newTask = { id: Date.now(), text: this.textInput.current.value };
    this.setState(prev => ({ tasks: [...prev.tasks, newTask] }));
    this.textInput.current.value = '';
  };

  removeTask = (id) => {
    this.setState(prev => ({
      tasks: prev.tasks.filter(task => task.id !== id),
    }));
  };

  render() {
    return (
      <div>
        <input ref={this.textInput} />
        <button onClick={this.addTask}>Add</button>
        <ul>
          {this.state.tasks.map(task => (
            <li key={task.id}>
              {task.text}
              <button onClick={() => this.removeTask(task.id)}>Del</button>
            </li>
          ))}
        </ul>
      </div>
    );
  }
}

Props

Props are read‑only data passed from parent to child. In functional components:

function Greeting({ name, age }) {
  return <p>{name}, {age} years old</p>;
}

In class components access via this.props.

PropTypes (install prop-types) validate types:

import PropTypes from 'prop-types';

Greeting.propTypes = {
  name: PropTypes.string.isRequired,
  age: PropTypes.number,
};

Default values can be provided via defaultProps or destructuring defaults.

Controlled vs. Uncontrolled Components

Controlled: value derived from state, updated via onChange:

const [email, setEmail] = useState('');
<input value={email} onChange={e => setEmail(e.target.value)} />

Uncontrolled: use refs to read values on demand; use defaultValue for initial value:

const fileRef = useRef();
<input type="file" ref={fileRef} />

Communication Between Components

  • Parent → child: props
  • Child → parent: callback functions passed via props
  • Cross‑component: Context API or state management libraries

Context API

Create and provide a context value:

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

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

Consume in class component via ThemeContext.Consumer or in functional component via useContext.

Lifecycle Methods (Class Components)

Key methods:

  • componentDidMount – after first render
  • componentDidUpdate(prevProps, prevState) – after update
  • componentWillUnmount – cleanup
  • shouldComponentUpdate(nextProps, nextState) – performance control

Modern alternatives: getDerivedStateFromProps, getSnapshotBeforeUpdate, and PureComponent.

Hooks

useState

const [items, setItems] = useState([]);

Remember: setter functions do not merge objects automatically; spread previous state when needed.

useEffect

Synchronise with external systems:

useEffect(() => {
  const subscription = dataSource.subscribe();
  return () => subscription.unsubscribe();
}, [dataSource]);

Dependency array controls re‑execution. Omit it to run after every render; pass [] to run once.

useLayoutEffect

Runs synchronously after DOM mutations but before paint; useful for measurements.

useCallback & useMemo

useCallback returns a memoized function; useMemo returns a memoized value. Both help avoid unnecessary re‑renders.

const expensiveValue = useMemo(() => compute(items), [items]);
const stableHandler = useCallback(() => doSomething(id), [id]);

useRef

Holds a mutable value that persists across renders without triggering re‑renders. Also used for DOM references.

useContext

Access context value directly:

const theme = useContext(ThemeContext);

useReducer

Alternative to useState for complex state logic:

const [state, dispatch] = useReducer(reducer, initialState);

Combine with useContext for global state.

Custom Hooks

Extract reusable logic; names must start with use.

React Router v6

Install: npm install react-router-dom@6

Basic setup:

import { BrowserRouter, Routes, Route } from 'react-router-dom';

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="*" element={<NotFound />} />
      </Routes>
    </BrowserRouter>
  );
}

Nested routes use <Outlet />:

<Route path="dashboard" element={<Dashboard />}>
  <Route index element={<Overview />} />
  <Route path="stats" element={<Stats />} />
</Route>

Navigation:

  • Declarative: <NavLink to="/home">Home</NavLink>
  • Programmatic: const navigate = useNavigate(); navigate('/home');

Route parameters: useParams(); query strings: useSearchParams(); state: pass via navigate(path, { state: {...} }) and read with useLocation().

Lazy loading:

const LazyComponent = React.lazy(() => import('./Component'));

<React.Suspense fallback={<div>Loading...</div>}>
  <LazyComponent />
</React.Suspense>

Protected routes: wrap target component with an authentication check.

State Management with Redux

Core Principles

  • Single source of truth (store)
  • State is read‑only, changed only by dispatching actions
  • Pure reducer functions compute new state

Basic Setup

import { createStore } from 'redux';

const initialState = { visible: true };

function visibilityReducer(state = initialState, action) {
  switch (action.type) {
    case 'SHOW':
      return { ...state, visible: true };
    case 'HIDE':
      return { ...state, visible: false };
    default:
      return state;
  }
}

const store = createStore(visibilityReducer);
export default store;

Connecting React Components

Use react-redux Provider, useSelector, and useDispatch:

// index.js
<Provider store={store}>
  <App />
</Provider>

// Component
import { useSelector, useDispatch } from 'react-redux';
const visible = useSelector(state => state.visible);
const dispatch = useDispatch();

Combining Reducers

import { combineReducers } from 'redux';
const rootReducer = combineReducers({ visibility: visibilityReducer, user: userReducer });

Middleware for Async Actions

Redux Thunk: allows action creators to return functions:

const fetchData = () => async dispatch => {
  const data = await api.get();
  dispatch({ type: 'LOAD_SUCCESS', payload: data });
};

Redux Saga: uses generator functions for more complex async flows.

Redux Toolkit (RTK)

Simplifies Redux with createSlice and configureStore:

import { createSlice, configureStore } from '@reduxjs/toolkit';

const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 },
  reducers: {
    incremented: state => { state.value += 1; },
    decremented: state => { state.value -= 1; },
  },
});

export const { incremented, decremented } = counterSlice.actions;
const store = configureStore({ reducer: { counter: counterSlice.reducer } });

Enables mutative logic inside reducers via Immer.

Persistence can be added with redux‑persist.

MobX as an Alternative

Observable state and automatic tracking:

import { makeAutoObservable } from 'mobx';

class CartStore {
  items = [];

  constructor() {
    makeAutoObservable(this);
  }

  addItem(item) {
    this.items.push(item);
  }
}

Use observer HOC (class) or useObserver (functional) to re‑render on changes.

Immutable.js

Provides persistent data structures. Often replaced by modern JavaScript spread syntax or Immer.

TypeScript with React

Create a project:

npx create-react-app my-app --template typescript

Typing props and state:

interface Props {
  title: string;
  count?: number;
}

const Header: React.FC<Props> = ({ title, count = 0 }) => (
  <h1>{title} - {count}</h1>
);

For class components:

interface State {
  loading: boolean;
}

class DataView extends React.Component<Props, State> { ... }

Typing Redux requires declaring store types and using typed hooks.

Supplementary Techniques

  • Portals: ReactDOM.createPortal(child, domNode) renders outside parent hierarchy.
  • forwardRef: pass ref through to a child component.
  • Performance: React.memo for functional components, PureComponent for class components.
  • Event Bus: simple event emitter for decoupled communication (use sparinlgy).

Frameworks: Dva.js & Umi.js

Dva.js (deprecated) and Umi.js provide integrated routing, state management (Dva model), and mock capabilities out of the box. Umi uses convention‑based routing and a centralized configuration file (.umirc.ts).

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.