React Part 2: Intermediate Concepts
Published: 2024-03-25 09:21:56

- **Index**:
    - Lifecycle Methods
        - componentDidMount
        - componentDidUpdate
        - componentWillUnmount
        - Hooks (useEffect)
    - Forms and Inputs
        - Controlled Components
        - Uncontrolled Components
        - Form Validation
    - Styling
        - Inline Styles
        - CSS Modules
        - Styled Components
    - Context API
        - createContext
        - useContext
        - Context Provider and Consumer

Thank you for joining us on this exciting journey through **React Part 2: Intermediate Concepts**! In this comprehensive guide, we’ll explore essential advanced React development topics, including **Lifecycle Methods**, **Forms and Inputs**, **Styling**, and the powerful **Context API**.

Our goal is to simplify these complex concepts, making them accessible to all. No overload theory here — just practical insights and hands-on knowledge. Whether you’re a seasoned developer or a curious learner, this part promises to be both enlightening and informative.

I hope you find this piece enjoyable! 😊

## Lifecycle Methods

React components go through different phases during their existence. These lifecycle methods allow you to perform specific actions at different points in a component’s life cycle.

1. **`componentDidMount`**:
    - Called after a component has been rendered to the DOM.
    - Ideal for initialization tasks (e.g., data fetching, setting up event listeners).
    - Commonly used in class components.
2. **`componentDidUpdate`**:
    - Invoked after a component’s state or props change and it re-renders.
    - Useful for handling side effects when state or props update.
    - Often used for data synchronization.
3. **`componentWillUnmount`**:
    - Called before a component is removed from the DOM.
    - Cleanup tasks (e.g., removing event listeners) should be done here.
    - Important but not used as frequently as the others.
4. **`useEffect`** (Hook):
    - Replaces **`componentDidMount`**, **`componentDidUpdate`**, and **`componentWillUnmount`**.
    - Handles side effects in functional components.
    - Essential for managing state and effects.

<aside>
💡 Once you embrace **`useEffect`**, you can bid farewell to most of the traditional lifecycle methods.

</aside>

```jsx
// Before
componentDidMount() {
  console.log("Component has mounted!");
  // Other setup tasks...
}

// After
useEffect(() => {
  console.log("Component has mounted!");
  // Other setup tasks...
}, []);
```

**Additional Lifecycle Topics:**

- **`shouldComponentUpdate`** method allows you to control whether a component should re-render when its props or state change. Optimize performance by preventing unnecessary re-renders.
- **`getSnapshotBeforeUpdate`** method is called right before the changes from the virtual DOM are reflected in the actual DOM. Capture information (e.g., scroll position) before an update.

---

## Forms and Inputs in React

### 1. Controlled Components

Controlled components are React components where the form input elements (like text fields, checkboxes, etc.) are **controlled** by the component’s state. Here’s how they work:

- **Explanation**:
    - The component’s state holds the current value of the input.
    - When the input value changes (e.g., user types in a text field), the state is updated.
    - The input value is set explicitly using the **`value`** prop.

### 2. Uncontrolled Components

Uncontrolled components allow the input elements to manage their own state without relying on React’s state management. They are less common but useful in certain scenarios:

- **Explanation**:
    - The input elements manage their own state internally.
    - You can access their values directly using DOM references (e.g., **`ref.current.value`**).

### 3. Form Validation

Form validation ensures that user input meets specific criteria (e.g., required fields, valid email addresses). React provides various ways to handle validation:

- **Explanation**:
    - Use built-in HTML attributes like **`required`**, **`min`**, **`max`**, etc.
    - Implement custom validation logic in your components.

```jsx
import React, { Component } from 'react';

class MyForm extends Component {
  constructor(props) {
    super(props);
    this.inputRef = React.createRef(); // Create a ref for the uncontrolled component
    this.state = {
      inputValue: '',
    }; // Initialize state for the controlled component
  }

  handleInputChange = (event) => {
    this.setState({ inputValue: event.target.value }); // Update state with the new input value
  }

  handleSubmit = (event) => {
    event.preventDefault();
    console.log('Uncontrolled:', this.inputRef.current.value); // Log the value of the uncontrolled component
    console.log('Controlled:', this.state.inputValue); // Log the value of the controlled component
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
	      {/* Uncontrolled component */}
        <input 
          type="text"
          ref={this.inputRef} // Use the ref to access the input's value directly
        /> 
				
				{/* Controlled component with validation */}
        <input 
          type="text"
          value={this.state.inputValue} // The value of the input is tied to the state
          onChange={this.handleInputChange} // When the input changes, update the state
          required // The input is required (it can't be left empty)
          minLength="5" // The input must be at least 5 characters long
        /> 

        <button type="submit">Submit</button>
      </form>
    );
  }
}

export default MyForm;
```

Some additional best practices and considerations for working with forms in React:

- **Use Controlled Components**: It’s a recommended approach for managing form data in React. In controlled components, the React state manages the form data, making it easier to handle changes and errors.
- **Perform Real-time Validation**: Check user input as it’s entered to catch errors early. Highlight the specific issues (e.g., missing required fields, invalid formats) to guide users in correcting their input.
- **Submit Without Page Reloads**: Instead of traditional form submissions (which cause page reloads), use AJAX or fetch to send form data to the server.
- **Consider Libraries for Complex Forms**: For more complex forms, consider using libraries like **Formik**.

---

## **Styling**

### 1. **Inline Styles**

- **What?** Inline styles allow you to apply CSS directly to individual React elements using JavaScript objects.
- **Why?** Sometimes you need dynamic styling based on component state or props.
- **How?** Use the **`style`** attribute with an object containing CSS properties.

```jsx
const MyComponent = () => {
  const styles = {
    color: 'blue',
    fontSize: '16px',
    fontWeight: 'bold',
  };
  return <div style={styles}>Hello, React!</div>;
};
```

### 2. **CSS Modules**

- **What?** CSS Modules provide local scoping for CSS classes in React components.
- **Why?** Avoid global style conflicts and improve maintainability.
- **How?**
    1. Create a CSS file (e.g., **`styles.module.css`**).
    2. Import styles as an object in your component.
    3. Use the imported class names.

```jsx
// styles.module.css
.myButton {
  background-color: #f0f0f0;
  border: 1px solid #ccc;
}
// MyComponent.js
import React from 'react';
import styles from './styles.module.css';

const MyComponent = () => {
  return <button className={styles.myButton}>Click me</button>;
};
```

### 3. **Styled Components**

- **What?** Styled Components is a library that allows you to write CSS-in-JS using tagged template literals.
- **Why?** Encapsulate styles within components and create reusable styled elements.
- **How?** Install **`styled-components`** and use it to define styled components.

```jsx
import styled from 'styled-components';

const StyledButton = styled.button`
  background-color: #007bff;
  color: white;
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
`;

const MyComponent = () => {
  return <StyledButton>Styled Button</StyledButton>;
};
```

### 4. **CSS-in-JS Libraries**

- **What?** CSS-in-JS libraries allow you to write CSS directly in your JavaScript code.
- **Why?** They offer dynamic styling, better scoping, and often integrate well with React.
- **Popular Libraries:**
    - **Emotion**: Provides a powerful and expressive way to style components.
    - **JSS (JavaScript Style Sheets)**: Allows you to define styles using JavaScript objects.
    - **Aphrodite**: Developed by Facebook, it generates unique class names for each component.
    - **Glamorous**: A simple and declarative CSS-in-JS library.

```jsx
import { css } from '@emotion/react';

const MyComponent = () => {
  const buttonStyles = css`
    background-color: #ff6347;
    color: white;
    padding: 8px 16px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
  `;

  return <button css={buttonStyles}>Emotion Button</button>;
};
```

### 5. **CSS Preprocessors**

- **What?** CSS preprocessors like **Sass** and **Less** enhance CSS by adding features like variables, nesting, and mixins.
- **Why?** They improve code organization and maintainability.

```jsx
// styles.scss
$primaryColor: #0074d9;

.myButton {
  background-color: $primaryColor;
  color: white;
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
```

Styling in React involves various approaches, and each has its use cases. Choose the one that best fits your project and keeps your codebase clean and maintainable.

---

## **Context API**

The Context API empowers you to manage global state efficiently. 

### 1. **`createContext`**

- **What?** **`createContext`** is a method provided by React that creates a new context.
- **Why?** It allows you to share data (state) across components without prop drilling.

```jsx
const MyContext = React.createContext(); // Create a context

const ParentComponent = () => { // In a parent component
  const sharedData = 'Hello from Context!';
  return (
    <MyContext.Provider value={sharedData}>
      <ChildComponent />
    </MyContext.Provider>
  );
};

const ChildComponent = () => { // In a child component
  const dataFromContext = React.useContext(MyContext);
  return <div>{dataFromContext}</div>;
};
```

### 2. **`useContext` Hook**

- **What?** The **`useContext`** hook allows functional components to consume context values.
- **Why?** It simplifies accessing context data within components.

```jsx
const MyComponent = () => {
  const dataFromContext = React.useContext(MyContext);
  return <div>{dataFromContext}</div>;
};
```

### 3. **Context Provider and Consumer**

- **What?**
    - **Provider:** Wraps components that need access to context data.
    - **Consumer:** Consumes context data within components.
- **Why?** They establish the context flow.

```jsx
const ParentComponent = () => { // In a parent component
  const sharedData = 'Hello from Context!';
  return (
    <MyContext.Provider value={sharedData}>
      <ChildComponent />
    </MyContext.Provider>
  );
};

const ChildComponent = () => { // In a child component
  return (
    <MyContext.Consumer>
      {(dataFromContext) => <div>{dataFromContext}</div>}
    </MyContext.Consumer>
  );
};
```

### 4. **Using Multiple Contexts**

- **What?** You can create and use multiple contexts in your application.
- **Why?** Sometimes you need separate contexts for different pieces of data.

```jsx
const UserContext = React.createContext();
const ThemeContext = React.createContext();
const App = () => {
  return (
    <UserContext.Provider value="John">
      <ThemeContext.Provider value="dark">
        <Profile />
      </ThemeContext.Provider>
    </UserContext.Provider>
  );
};
```

### 6. **Context with Reducers**

- **What?** Combine context with reducers (similar to Redux) for more complex state management.
- **Why?** Provides a structured way to handle state changes.

```jsx
const UserReducer = (state, action) => {
  switch (action.type) {
    case 'SET_USER':
      return { ...state, user: action.payload };
    default:
      return state;
  }
};

const UserContext = React.createContext();

const App = () => {
  const [state, dispatch] = React.useReducer(UserReducer, { user: 'John' });

  return (
    <UserContext.Provider value={{ state, dispatch }}>
      <Profile />
    </UserContext.Provider>
  );
};
```

Mastering the React Context API opens up powerful possibilities for managing state and sharing data across components. Remember to choose the right approach based on your project’s needs.

Remember, learning React is a journey, and each step brings you closer to becoming a proficient developer. Stay curious, keep coding, and look forward to Part 3! 🚀

---

📝 Originally published at https://www.rajondey.com/articles/

Connect with me:
- 🐦 Twitter: https://twitter.com/rajjon_dey
- 💼 LinkedIn: https://www.linkedin.com/in/rajondey/
- 💻 Github: https://github.com/RajonDey