Published on

React.js Complete Guide

Authors

Core Concepts

Components and Props

// Functional 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>;
  }
}

// Props Example
const element = <Welcome name="John" />;

State and Lifecycle

function Counter() {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    document.title = `Count: ${count}`;
    
    return () => {
      // cleanup
    };
  }, [count]);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
    </div>
  );
}

Hooks

// useState
function NameForm() {
  const [name, setName] = useState('');
  return (
    <input 
      value={name}
      onChange={e => setName(e.target.value)}
    />
  );
}

// useEffect
function DataFetcher() {
  const [data, setData] = useState(null);
  
  useEffect(() => {
    fetchData().then(setData);
  }, []);
  
  return data ? <div>{data}</div> : <div>Loading...</div>;
}

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

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button className={theme}>Themed Button</button>;
}

// useReducer
function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, { count: 0 });
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
    </>
  );
}

Event Handling

function ActionButton() {
  const handleClick = (e) => {
    e.preventDefault();
    console.log('Button clicked');
  };
  
  return (
    <button onClick={handleClick}>
      Click me
    </button>
  );
}

Advanced Patterns

Context API

// Create context
const ThemeContext = React.createContext({
  theme: 'light',
  toggleTheme: () => {}
});

// Provider
function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
  
  const toggleTheme = () => {
    setTheme(theme === 'light' ? 'dark' : 'light');
  };
  
  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

// Consumer
function ThemedComponent() {
  const { theme, toggleTheme } = useContext(ThemeContext);
  return (
    <div className={theme}>
      <button onClick={toggleTheme}>Toggle Theme</button>
    </div>
  );
}

Custom Hooks

// Custom Hook for form handling
function useForm(initialValues) {
  const [values, setValues] = useState(initialValues);
  
  const handleChange = (e) => {
    const { name, value } = e.target;
    setValues(prev => ({
      ...prev,
      [name]: value
    }));
  };
  
  const resetForm = () => {
    setValues(initialValues);
  };
  
  return [values, handleChange, resetForm];
}

// Usage
function SignupForm() {
  const [values, handleChange, resetForm] = useForm({
    username: '',
    email: '',
    password: ''
  });
  
  const handleSubmit = (e) => {
    e.preventDefault();
    console.log(values);
    resetForm();
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input
        name="username"
        value={values.username}
        onChange={handleChange}
      />
      {/* Other fields */}
    </form>
  );
}

HOC (Higher Order Components)

// HOC Example
function withSubscription(WrappedComponent, selectData) {
  return function(props) {
    const [data, setData] = useState(null);
    
    useEffect(() => {
      const subscription = DataSource.subscribe(data => {
        setData(selectData(data, props));
      });
      
      return () => subscription.unsubscribe();
    }, [props]);
    
    return <WrappedComponent data={data} {...props} />;
  };
}

// Usage
const CommentListWithSubscription = withSubscription(
  CommentList,
  (DataSource, props) => DataSource.getComments()
);

Render Props

class Mouse extends React.Component {
  state = { x: 0, y: 0 };
  
  handleMouseMove = (event) => {
    this.setState({
      x: event.clientX,
      y: event.clientY
    });
  };
  
  render() {
    return (
      <div onMouseMove={this.handleMouseMove}>
        {this.props.render(this.state)}
      </div>
    );
  }
}

// Usage
<Mouse render={({ x, y }) => (
  <h1>Mouse position: {x}, {y}</h1>
)}/>

Performance Optimization

React.memo

const MemoizedComponent = React.memo(function MyComponent(props) {
  return (
    <div>{props.name}</div>
  );
});

useMemo and useCallback

function ExpensiveComponent({ data, onItemSelect }) {
  // Memoize expensive calculations
  const processedData = useMemo(() => {
    return expensiveOperation(data);
  }, [data]);
  
  // Memoize callbacks
  const handleSelect = useCallback((item) => {
    onItemSelect(item.id);
  }, [onItemSelect]);
  
  return (
    <div>
      {processedData.map(item => (
        <Item 
          key={item.id}
          item={item}
          onSelect={handleSelect}
        />
      ))}
    </div>
  );
}

Code Splitting

// Dynamic Import
const OtherComponent = React.lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <OtherComponent />
    </Suspense>
  );
}

State Management

Redux Integration

// Actions
const increment = () => ({
  type: 'INCREMENT'
});

// Reducer
function counterReducer(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    default:
      return state;
  }
}

// Component
function Counter({ count, increment }) {
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

// Connect to Redux
export default connect(
  state => ({ count: state }),
  { increment }
)(Counter);

Zustand Example

import create from 'zustand';

const useStore = create((set) => ({
  count: 0,
  increment: () => set(state => ({ count: state.count + 1 })),
  decrement: () => set(state => ({ count: state.count - 1 }))
}));

function Counter() {
  const { count, increment } = useStore();
  return (
    <button onClick={increment}>
      Count: {count}
    </button>
  );
}

Testing

// Component Test
import { render, fireEvent } from '@testing-library/react';

test('increments counter', () => {
  const { getByText } = render(<Counter />);
  const button = getByText(/increment/i);
  
  fireEvent.click(button);
  
  expect(getByText(/count: 1/i)).toBeInTheDocument();
});

// Hook Test
import { renderHook, act } from '@testing-library/react-hooks';

test('useCounter', () => {
  const { result } = renderHook(() => useCounter());
  
  act(() => {
    result.current.increment();
  });
  
  expect(result.current.count).toBe(1);
});

Error Handling

class ErrorBoundary extends React.Component {
  state = { hasError: false };
  
  static getDerivedStateFromError(error) {
    return { hasError: true };
  }
  
  componentDidCatch(error, errorInfo) {
    logErrorToService(error, errorInfo);
  }
  
  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}

// Usage
<ErrorBoundary>
  <MyComponent />
</ErrorBoundary>

Routing

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

function App() {
  return (
    <BrowserRouter>
      <nav>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
      </nav>
      
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/users/:id" element={<UserProfile />} />
        <Route path="*" element={<NotFound />} />
      </Routes>
    </BrowserRouter>
  );
}

Resources

Official Documentation

Learning Resources

Tools