You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

React项目中如何复用Loader组件在表单提交、模态框打开时显示加载状态?

How to Reuse the Loader Component for Custom Actions Like Form Submissions or Modal Openings

Got it, let's break down how to adapt your existing Loader and Layout components to support manual control over the loading state for custom actions. Here's a step-by-step solution:

1. Convert the Loader to a Controlled Component

First, we need to take the isLoading state out of the Loader itself and let it receive this state from a parent component via props. This makes the Loader flexible to external control:

// ./common/Loader.js
import React, { Component } from "react";

export class Loader extends Component {
  render() {
    // Use isLoading from props instead of internal state
    const { isLoading } = this.props;
    return (
      <span className={"loading " + (isLoading ? "show" : "hide")}>
        <span className="loading-bg"></span>{" "}
        <img src="img/loader.gif" alt="Loading" />{" "}
      </span>
    );
  }
}

export default Loader;

2. Manage Loader State in Layout & Add Global Control via Context

To let any child component trigger the Loader, we'll use React Context to expose show/hide methods globally. First, create a Context file:

// ./LoaderContext.js
import React from 'react';

// Create a Context with default empty functions
const LoaderContext = React.createContext({
  showLoader: () => {},
  hideLoader: () => {}
});

export default LoaderContext;

Now update the Layout component to manage the isLoading state, handle the initial auto-hide, and provide the control methods via Context:

// ./Layout.js
import React, { Component } from "react";
import { Container } from "reactstrap";
import { NavMenu } from "./NavMenu";
import Loader from "./common/Loader";
import Footer from "./Footer";
import LoaderContext from './LoaderContext';

export class Layout extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isLoading: true // Keep initial loading state for page load
    };
  }

  componentDidMount() {
    // Retain the original auto-hide behavior for initial page load
    setTimeout(() => {
      this.setState({ isLoading: false });
    }, 500);
  }

  // Method to show the Loader
  showLoader = () => {
    this.setState({ isLoading: true });
  };

  // Method to hide the Loader
  hideLoader = () => {
    this.setState({ isLoading: false });
  };

  render() {
    const contextValue = {
      showLoader: this.showLoader,
      hideLoader: this.hideLoader
    };

    return (
      <LoaderContext.Provider value={contextValue}>
        <div>
          <NavMenu />
          <Container fluid>
            {/* Pass the controlled isLoading state to Loader */}
            <Loader isLoading={this.state.isLoading} />
            {this.props.children}
            <footer className="d-flex justify-content-between align-items-center pt-3">
              <Footer />
            </footer>
          </Container>
        </div>
      </LoaderContext.Provider>
    );
  }
}

3. Use the Loader Control Methods in Child Components

Now any child component (like a form, modal trigger, etc.) can access the show/hide methods via Context to control the Loader.

Example: Form Submission (Class Component)

// ./components/SubmitForm.js
import React, { Component } from "react";
import LoaderContext from '../LoaderContext';

export class SubmitForm extends Component {
  // Attach the Context to the class component
  static contextType = LoaderContext;

  handleSubmit = async (e) => {
    e.preventDefault();
    
    // Show Loader when submission starts
    this.context.showLoader();

    try {
      // Simulate an API call for form submission
      const response = await fetch('/api/submit-form', {
        method: 'POST',
        body: new FormData(e.target)
      });
      const result = await response.json();
      
      // Handle success (e.g., show alert, reset form)
      alert('Form submitted successfully!');
      e.target.reset();
    } catch (error) {
      // Handle errors
      console.error('Submission failed:', error);
      alert('Oops, something went wrong. Please try again.');
    } finally {
      // Hide Loader regardless of success/failure
      this.context.hideLoader();
    }
  };

  render() {
    return (
      <form onSubmit={this.handleSubmit} className="mt-4">
        <div className="mb-3">
          <label htmlFor="username" className="form-label">Username</label>
          <input type="text" className="form-control" id="username" name="username" required />
        </div>
        <button type="submit" className="btn btn-primary">Submit</button>
      </form>
    );
  }
}

Example: Modal Trigger (Function Component with Hooks)

If you're using function components, you can use the useContext hook:

// ./components/ModalTrigger.js
import React, { useContext, useState } from "react";
import LoaderContext from '../LoaderContext';

export function ModalTrigger() {
  const { showLoader, hideLoader } = useContext(LoaderContext);
  const [modalData, setModalData] = useState(null);
  const [isModalOpen, setIsModalOpen] = useState(false);

  const openModal = async () => {
    // Show Loader while fetching modal data
    showLoader();

    try {
      // Simulate fetching data for the modal
      const response = await fetch('/api/modal-content');
      const data = await response.json();
      setModalData(data);
      setIsModalOpen(true);
    } catch (error) {
      console.error('Failed to load modal data:', error);
      alert('Could not load modal content.');
    } finally {
      // Hide Loader once data is loaded (or failed)
      hideLoader();
    }
  };

  return (
    <div>
      <button onClick={openModal} className="btn btn-secondary">Open Modal</button>
      
      {/* Modal (simplified) */}
      {isModalOpen && modalData && (
        <div className="modal show d-block">
          <div className="modal-dialog">
            <div className="modal-content">
              <div className="modal-header">
                <h5 className="modal-title">{modalData.title}</h5>
                <button onClick={() => setIsModalOpen(false)} className="btn-close"></button>
              </div>
              <div className="modal-body">{modalData.content}</div>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

Alternative: Pass Methods via Props (For Shallow Component Trees)

If your component tree isn't deeply nested, you can skip Context and pass the showLoader/hideLoader methods directly through props from Layout to child components. However, Context is cleaner for larger apps with deep nesting.


内容的提问来源于stack exchange,提问作者Ligori Jeba Raja

火山引擎 最新活动