Skip to content

This Repo's readme has a bunch of examples that highlight the concepts covered in the CEN 3031 React Discussion section

Notifications You must be signed in to change notification settings

UFWebApps2-0/React-Examples

 
 

Repository files navigation

Certainly! Let's dive into understanding why one might choose React and the benefits it offers.

1. Declarative UI

React is declarative, which means you describe how the UI should look based on different states, rather than specifying how to achieve that look. This makes your code more predictable and easier to debug.

Example:

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

Here, instead of writing procedures to manipulate the DOM directly, you just describe the final UI in terms of components.

Notes:

  • React abstracts away direct DOM manipulations.
  • Focus is on the desired state, not the transitions.

2. Component-Based Architecture

React encourages building UIs by breaking them down into small, reusable pieces called components. Each component manages its own state and rendering, leading to modular and maintainable codebases.

Example:

function UserProfile({ user }) {
  return (
    <div>
      <Avatar src={user.avatarUrl} />
      <div>{user.name}</div>
    </div>
  );
}

Notes:

  • Components can be reused in various parts of the application.
  • Each component is a self-contained unit.

3. Virtual DOM

React creates a virtual representation of the UI in memory. When state changes, React efficiently updates the virtual DOM first, then calculates the difference with the previous version and makes minimal updates to the real DOM.

Example: Suppose you have a list of items, and only one item changes. Instead of re-rendering the entire list, React will only update the changed item in the DOM.

Notes:

  • Efficient and optimized rendering process.
  • Minimizes direct DOM manipulations which can be costly.

4. Strong Ecosystem and Community Support

React has a vast ecosystem of libraries, tools, and extensions. The community is large and vibrant, providing an abundance of resources, tutorials, and solutions for common problems.

Example: For state management, libraries like Redux or MobX can be integrated with React. For routing, react-router is a popular choice.

Notes:

  • High community engagement means quick help on platforms like Stack Overflow.
  • Continuous evolution and improvement.

5. Flexibility with Platforms and Integration

React isn’t limited to web development. With React Native, for instance, you can develop mobile applications. The skills you acquire in React can thus be applied across different platforms.

Example: If you have a React web application and decide to make a mobile version, you can leverage a lot of your existing knowledge and even some of your components with React Native.

Notes:

  • Code reuse between platforms.
  • Unified development experience.

6. Backed by Facebook

React has the backing of a large corporation, Facebook. This not only means that it’s actively maintained, but also that it's used in large-scale applications, ensuring its robustness.

Example: Facebook’s main application itself uses React, proving its scalability and performance.

Notes:

  • Assurance of longevity and support.
  • Continuously battle-tested at scale.

In conclusion, React offers a combination of efficiency, modularity, and flexibility that makes it a powerful tool for developers. The strong community and backing by Facebook further solidify its place as a leading choice for web and mobile application development.

Components are fundamental building blocks in React applications. They let you split the UI into independent, reusable pieces, and think about each piece in isolation.

1. Functional Components

These are the simplest form of React components and are just JavaScript functions that return JSX. They can receive props as arguments.

Example:

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

// Usage
<Welcome name="React" />

Notes:

  • Usually stateless (though they can have state with React Hooks).
  • Often used for presentational components.

2. Class Components

Prior to the introduction of Hooks in React 16.8, class components were the primary way to manage state and lifecycle methods in React. They extend the React.Component class.

Example:

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

// Usage
<Welcome name="React" />

Notes:

  • Can have local state and lifecycle methods.
  • More verbose than functional components.

3. Components with State (Using Hooks)

With the introduction of Hooks, functional components can now manage their own state without converting to class components.

Example:

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  );
}

Notes:

  • useState is a Hook that lets you add state to functional components.
  • Hooks cannot be used inside class components.

4. Higher-Order Components (HOCs)

A higher-order component is a function that takes a component and returns a new component with additional properties or functionalities.

Example:

function withLogging(WrappedComponent) {
  return class extends React.Component {
    componentDidMount() {
      console.log(`${WrappedComponent.name} has mounted`);
    }
    render() {
      return <WrappedComponent {...this.props} />;
    }
  };
}

const LoggedWelcome = withLogging(Welcome);

// Usage
<LoggedWelcome name="React" />

Notes:

  • Useful for code reusability when multiple components share common logic.
  • HOCs can be chained together.

5. Render Props

A component with a render prop takes a function that returns a React element and calls it instead of implementing its own render logic.

Example:

class MouseTracker extends React.Component {
  state = { x: 0, y: 0 };

  handleMouseMove = (event) => {
    this.setState({
      x: event.clientX,
      y: event.clientY
    });
  };

  render() {
    return (
      <div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
        {this.props.render(this.state)}
      </div>
    );
  }
}

// Usage
<MouseTracker render={mouse => (
  <h1>The mouse position is ({mouse.x}, {mouse.y})</h1>
)}/>

Notes:

  • Useful for sharing behavior between React components.
  • Encourages component composition.

Components form the heart of any React application. They enable modularity, reusability, and efficient code organization. Whether you're using functional components with Hooks, class components, HOCs, or render props, React offers a range of patterns to create dynamic and interactive user interfaces.

In React, state refers to a data structure that represents part of an app's memory. It's local to a specific component and determines that component's behavior and render output. State is mutable, which means it can be changed, and when it does change, React re-renders the component to reflect those changes.

State makes it possible to create dynamic and interactive components.

Key Characteristics of State:

  1. Local: State is component-specific. A component's state cannot be accessed or modified directly by a sibling or parent component, but it can be passed down to children as props.

  2. Mutable: Unlike props, which are read-only, state can be changed. This is typically done using the setState method in class components or the useState hook in functional components.

  3. Reactivity: When state changes, the component re-renders.

Examples:

1. Using State in a Class Component:

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

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

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.increment}>Increment</button>
      </div>
    );
  }
}

Notes:

  • this.state is initialized in the constructor.
  • setState method is used to update the state.

2. Using State in a Functional Component with the useState Hook:

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  const increment = () => {
    setCount(count + 1);
  };

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

Notes:

  • useState returns the current state and a function to update it. We destructure it into count and setCount.
  • State update is done using setCount.

In both examples, the state (count) determines the displayed number, and when the state changes (via the button click), the component re-renders to reflect the updated state.

To summarize, state in React provides a way to store changing values within a component. It allows for the creation of dynamic and interactive user interfaces that can respond to user actions and other events.

Hooks introduced in React 16.8 provided a new way to use state and other React features without writing a class component. Among the numerous hooks provided by React, the useState hook allows functional components to have state.

useState Hook:

The useState hook is a function that returns a pair: the current state value and a function to update it. This hook makes it possible for functional components to have their own local state.

Syntax:

const [state, setState] = useState(initialState);
  • state: The current value of the state.
  • setState: A function to update the state.
  • initialState: The initial value of the state (can be a static value or a function).

Example:

Let's consider a simple counter application using the useState hook:

import React, { useState } from 'react';

function Counter() {
  // Declare state variables
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Current Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <button onClick={() => setCount(count - 1)}>Decrement</button>
    </div>
  );
}

In the example:

  • We initialize the count state variable with a value of 0.
  • The setCount function is used to update the count state.
  • Clicking on the "Increment" button will increase the count, and the "Decrement" button will decrease the count.

Notes:

  1. Function Updates: If the new state is computed using the previous state, you can pass a function to setState. This function will receive the previous state as its first argument.

    Example:

    setCount(prevCount => prevCount + 1);
  2. Multiple State Variables: Unlike class components that have a single this.state object, with hooks, you can use multiple useState calls to declare different state variables.

    Example:

    const [age, setAge] = useState(25);
    const [name, setName] = useState("John");
  3. Lazy Initial State: If the initial state is the result of some computation, you can provide a function to useState which will run only on the initial render.

    Example:

    const [someExpensiveValue] = useState(() => {
      return computeExpensiveValue();
    });
  4. Re-renders: Each time a state is updated using its associated setter function (e.g., setCount), the component re-renders.

To wrap up, the useState hook has provided a much simpler and cleaner way to manage state in functional components, eliminating the need for class components in many situations and making the codebase more modern and easier to understand.

In React, props is short for "properties". They are a way of passing data from parent to child components. Essentially, props allow components to communicate with each other. Props are read-only and help you ensure that components are used as intended.

Key Characteristics of Props:

  1. Read-Only: Props are immutable, meaning that a component cannot change its own props. This ensures one-way data flow from parent to child, leading to predictable component behavior.

  2. Data Passing: Props make it possible to pass data from a parent component to a child component.

  3. Functional and Class Components: Both functional and class components can receive and use props.

Examples:

1. Passing and Using Props in Functional Components:

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

// Using the component
<Welcome name="React" />

In this example, the Welcome component receives a name prop and displays it.


2. Passing and Using Props in Class Components:

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}!</h1>;
  }
}

// Using the component
<Welcome name="React" />

In class components, props are accessed with this.props.


3. Passing Multiple Props:

function UserInfo(props) {
  return (
    <div>
      <h1>{props.name}</h1>
      <p>Age: {props.age}</p>
    </div>
  );
}

// Using the component
<UserInfo name="John Doe" age={30} />

Here, multiple props (name and age) are passed to the UserInfo component.


4. Passing Children Prop:

The content between the opening and closing tags of a component invocation is passed as a special prop called children.

function Container(props) {
  return <div className="container">{props.children}</div>;
}

// Using the component
<Container>
  <p>This is some content inside the container.</p>
</Container>

The children prop is very powerful and can be used to pass JSX, components, or even functions.


5. Destructuring Props:

For better readability and cleaner code, you can destructure props directly in the function parameters.

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

// Using the component
<Welcome name="React" />

Notes:

  • Props promote the reuse of components. By passing different data as props, you can get varied output from the same component.
  • Props, being read-only, ensure the principle of unidirectional data flow, making data changes in your application more predictable.
  • Always use props to pass data from parent to child components. If you need more complex state management or interactions between non-parent-child components, consider using context or state management libraries like Redux.

In summary, props are a fundamental aspect of React, enabling components to be modular, reusable, and interactive. They provide a mechanism for passing data and callbacks among components.

Both state and props are core concepts in React that allow for dynamic and interactive user interfaces. However, they serve different purposes and have different characteristics. Let's compare them to better understand their roles and distinctions.

State:

  1. Source: State originates from within the component and represents data that may change over time.

  2. Mutability: State is mutable. You can change a component's state using the setState method in class components or the useState setter in functional components.

  3. Purpose: State typically represents user-interactable data, like the value of an input field or a switch's toggle status.

  4. Reactivity: When state changes, the component re-renders.

  5. Scope: State is local and private to the component unless it's lifted up or shared with other components using mechanisms like context or global state managers.

Props:

  1. Source: Props are passed into a component from its parent. They are used to pass data down the component tree.

  2. Immutability: Props are read-only. A component cannot modify its own props.

  3. Purpose: Props allow for component reusability and parameterization. For instance, you could have a Button component that can take different labels and actions as props.

  4. Reactivity: Although a component can't change its own props, when the parent component changes the value of the props, the child component re-renders.

  5. Scope: Props are the mechanism by which data is passed between components, allowing for a parent-child data flow.

Example:

Imagine a user profile component:

  • Using State: The profile component might have a state called isEditMode to toggle between view mode and edit mode.
const [isEditMode, setIsEditMode] = useState(false);
  • Using Props: The same profile component might receive a user's details via props.
function UserProfile(props) {
  return (
    <div>
      <h1>{props.userName}</h1>
      <p>{props.userEmail}</p>
    </div>
  );
}

<UserProfile userName="John Doe" userEmail="[email protected]" />

Notes:

  1. Initialization: State needs initialization (e.g., this.state = { ... } in class components or useState(initialValue) in functional components). Props, on the other hand, are initialized by the parent component and received by the child component.

  2. Lifecycle: State changes can be asynchronous, especially when using setState in class components. Props are deterministic and reflect what the parent component provides.

  3. Use Case Determination: If a value is specific to a component and changes over time (like user interactions), it's likely a state. If a value is passed to a component to configure its behavior, it's a prop.

In essence, while both state and props influence what's rendered in a component, state is used for mutable, local data and interactions, whereas props allow for component configuration, communication, and data flow from parent to child.

State and props often interact in React applications to create dynamic and interactive user interfaces. While a component cannot alter its props directly, it can use props to initialize or influence its state. Conversely, a component can pass its state as props to its child components. Let's explore some common patterns of interaction between state and props with examples.

1. Initializing State from Props:

Sometimes, a component's initial state may depend on the props passed to it. In class components, this is typically done in the constructor.

Example with Class Component:

class UserProfile extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      username: props.username
    };
  }

  render() {
    return <div>{this.state.username}</div>;
  }
}

Example with Functional Component:

function UserProfile(props) {
  const [username, setUsername] = useState(props.username);
  return <div>{username}</div>;
}

Note: Initializing state from props is useful, but you should avoid duplicating data between state and props unless there's a specific reason. If the prop changes, the local state will not automatically update.


2. Passing State as Props to Child Components:

Parent components can share their state with child components by passing it down as props.

class Parent extends React.Component {
  constructor() {
    super();
    this.state = {
      message: "Hello from Parent!"
    };
  }

  render() {
    return <Child greeting={this.state.message} />;
  }
}

function Child(props) {
  return <div>{props.greeting}</div>;
}

3. Lifting State Up:

When two components need to share state, it's common to lift the state up to their closest common ancestor. This pattern helps maintain sync between the components.

class Parent extends React.Component {
  constructor() {
    super();
    this.state = {
      value: ""
    };
  }

  handleChange = (newValue) => {
    this.setState({ value: newValue });
  };

  render() {
    return (
      <>
        <ChildA value={this.state.value} onChange={this.handleChange} />
        <ChildB value={this.state.value} />
      </>
    );
  }
}

function ChildA(props) {
  return (
    <input 
      value={props.value} 
      onChange={(e) => props.onChange(e.target.value)} 
    />
  );
}

function ChildB(props) {
  return <div>Value from ChildA: {props.value}</div>;
}

In this example, ChildA can modify the state, and ChildB displays the state. The state is lifted up to Parent, ensuring both children remain in sync.


4. Passing Down Callbacks to Modify State:

Parent components can pass down callback functions as props to child components, allowing them to modify the parent's state.

class Parent extends React.Component {
  constructor() {
    super();
    this.state = {
      count: 0
    };
  }

  increment = () => {
    this.setState(prevState => ({ count: prevState.count + 1 }));
  };

  render() {
    return (
      <>
        <Counter value={this.state.count} onIncrement={this.increment} />
      </>
    );
  }
}

function Counter(props) {
  return (
    <div>
      <p>Count: {props.value}</p>
      <button onClick={props.onIncrement}>Increment</button>
    </div>
  );
}

In this example, the Parent component has a state count and a method increment. The method is passed down to Counter as a prop, allowing the child component to modify the parent's state.


Notes:

  • State-Props Dependency: It's essential to be cautious when using props to set state because this creates a dependency. If the prop changes outside the component, it won't automatically update the internal state initialized from that prop, potentially leading to bugs or stale data.

  • One-way Data Flow: Remember, in React, data flows one way (downward from parent to child) through props. Child components can't directly modify any props they receive, but they can notify the parent of desired changes via callbacks.

In essence, the interaction between state and props allows components to communicate, share, and synchronize data across the component tree, making React applications dynamic and responsive.

useEffect in React:

The useEffect hook is a feature in React (introduced in version 16.8) that allows you to perform side effects in function components. Side effects can include data fetching, subscriptions, manual DOM manipulations, and other effects that don't fit into the React data flow directly.

The useEffect hook takes two arguments:

  1. A function that contains the code to run the side effect.
  2. A dependency array that determines when the effect should run.

The hook can be used to replicate lifecycle behavior in class components, like componentDidMount, componentDidUpdate, and componentWillUnmount.

Examples of useEffect:

  1. Running an Effect After Every Render (similar to both componentDidMount and componentDidUpdate in class components):
useEffect(() => {
  console.log('This runs after every render');
});
  1. Running an Effect Only Once (similar to componentDidMount in class components):
useEffect(() => {
  console.log('This runs only once when the component mounts');
}, []);
  1. Running an Effect Based on Specific Data Changes:
const [count, setCount] = useState(0);

useEffect(() => {
  console.log('Count has changed:', count);
}, [count]);

In this example, the effect will run only when the count state changes.

  1. Cleaning Up an Effect (similar to componentWillUnmount in class components):
useEffect(() => {
  const subscription = someService.subscribe();

  return () => {
    subscription.unsubscribe(); // Cleanup function
  };
}, []);

The return function inside useEffect is used to clean up side effects. It runs before the component is removed from the UI or before rerunning the effect due to changes in the dependencies.

What are Side Effects?

In programming, side effects refer to changes in the system state or observable interactions with the outside world that occur as a result of executing some code. In the context of React, side effects can include:

  1. Data Fetching: Making an API call to fetch data.
  2. Subscriptions: Subscribing to some external data source, like a WebSocket.
  3. Timers: Setting up intervals or timeouts.
  4. Manual DOM Manipulations: Directly interacting with the DOM outside of the React data flow.
  5. Logging: Logging data to the console or sending telemetry.
  6. Persistent State: Interacting with local storage or session storage.

Notes:

  • Dependency Array: It's important to provide the correct dependencies to useEffect to ensure that it runs at the right times and not too often. Omitting dependencies or providing wrong ones can lead to bugs.

  • Separate Concerns: Instead of thinking in lifecycles, with hooks and effects, we can split code by what it's doing rather than when it's doing it. This can lead to clearer and more modular code.

  • Clean Up: Always remember to clean up side effects when they are no longer needed, like unsubscribing from subscriptions or clearing timers, to avoid potential memory leaks.

Overall, useEffect provides a powerful way to work with side effects in React function components, allowing for a more flexible and declarative approach compared to class lifecycle methods.

Lifecycle methods in React are special methods that automatically get called as your component achieves certain milestones in its life, from birth (mounting on the DOM) to death (unmounting from the DOM).

Class components in React have several lifecycle methods, which can be grouped based on the stage of the component's life:

1. Mounting:

These methods are called in the following order when an instance of a component is being created and inserted into the DOM:

  1. constructor(): The constructor for a React component is called before it is mounted. It's a good place to initialize state and bind event handlers.
  2. static getDerivedStateFromProps(): This method is called right before render(). It returns an object to update the state, or null to not update anything.
  3. render(): The only required method in a class component. It examines this.props and this.state and returns one of the following types: React elements, arrays and fragments, portals, string and numbers, booleans or null.
  4. componentDidMount(): Invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here, such as setting up subscriptions or fetching data.

2. Updating:

An update can be caused by changes to props or state. These methods are called in the following order when a component is being re-rendered:

  1. static getDerivedStateFromProps(): Same as in mounting.
  2. shouldComponentUpdate(nextProps, nextState): Allows the component to exit the update lifecycle if there's no reason to apply a new render. Returns a boolean value.
  3. render(): Re-renders the component.
  4. getSnapshotBeforeUpdate(prevProps, prevState): Captures some information (a snapshot) before the DOM is updated. Any value returned by this will be passed into componentDidUpdate().
  5. componentDidUpdate(prevProps, prevState, snapshot): Invoked immediately after updating occurs. Useful for operating on the DOM or performing network requests after an update.

3. Unmounting:

This method is called when a component is being removed from the DOM:

  1. componentWillUnmount(): Executed right before a component is unmounted and destroyed. It's the right place to perform any necessary cleanup, like invalidating timers or cleaning up subscriptions.

4. Error Handling:

These methods are called when there's an error during rendering, in a lifecycle method, or in the constructor of any child component:

  1. static getDerivedStateFromError(error): Called after an error has been thrown by a descendant component. It returns a value to update state.
  2. componentDidCatch(error, info): Catches JavaScript errors anywhere in the component tree, logs those errors, and displays a fallback UI.

Notes:

  • Deprecation: Some lifecycle methods like componentWillMount, componentWillUpdate, and componentWillReceiveProps have been deprecated in recent versions of React due to potential confusion and are unsafe with async rendering. They can still be used by prefixing them with UNSAFE_, but it's recommended to transition away from them.

  • Functional Components: With the introduction of hooks in React 16.8, functional components can now mimic the behavior of class lifecycle methods using effects (useEffect). This has led to a shift in the community towards functional components and hooks instead of class components.

  • Order Matters: The order in which the lifecycle methods are called is essential for understanding the flow of data and operations within a component, especially when debugging.

Remember that understanding the lifecycle methods and when they are called can be pivotal in ensuring your components behave as expected, especially when working with data fetching, updates based on prop changes, and cleanup operations.

About

This Repo's readme has a bunch of examples that highlight the concepts covered in the CEN 3031 React Discussion section

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • JavaScript 100.0%