开源日报每天推荐一个 GitHub 优质开源项目和一篇精选英文科技或编程文章原文,坚持阅读《开源日报》,保持每日学习的好习惯。
2024年1月2日,开源日报第1062期:
今日推荐开源项目:《llm-course》
今日推荐英文原文:《Mastering React.js: Best Practices and Design Patterns for High-Quality Applications


开源项目

今日推荐开源项目:《llm-course》传送门:项目链接

推荐理由:这个路线图和Colab notebooks可以帮助你学习大型语言模型(LLM)相关课程,LLM(大型语言模型)课程分为三个部分:

🧩 LLM基础知识 包含关于数学、Python 和神经网络的基本知识

🧑‍🔬 LLM科学家 专注于学习如何利用最新技术构建最佳的LLM

👷 LLM工程师 专注于如何创建基于LLM的解决方案并将其部署

Blog 网站直达 https://mlabonne.github.io/blog/


英文原文

今日推荐英文原文:Mastering React.js: Best Practices and Design Patterns for High-Quality Applications

推荐理由:React.js 已成为构建动态用户界面的首选库,其基于组件的架构和声明式语法使其成为创建可扩展且易于维护的应用程序的强大工具,本文概述了最佳实践和设计模式,如合理项目结构提高可扩展性; 明智使用状态管理,避免局部状态传递;采用函数组件和Hooks简化生命周期等等


Mastering React.js: Best Practices and Design Patterns for High-Quality Applications

React.js has become the go-to library for building dynamic user interfaces. Its component-based architecture and declarative syntax make it a powerful tool for creating scalable and maintainable applications. However, as your project grows, maintaining code quality and scalability becomes paramount. In this article, we’ll explore the best practices and design patterns in React.js that will help you build high-quality applications.

1. Project Structure: Organize for Scalability

A well-organized project structure is the foundation for a scalable React application. Consider structuring your project based on features or modules rather than file types. This modular approach makes it easier to locate and update code related to specific functionality.

Example:

/src
  /components
    Header.js
    Footer.js
  /pages
    Home.js
    About.js
  /services
    api.js
  /styles
    main.css
  App.js
  index.js

2. State Management: Use State Wisely

State is at the core of React applications. Effective state management is crucial for building scalable applications. Lift state up to the nearest common ancestor for shared state between components. Utilize state management libraries like Redux for complex state scenarios.

Example:

// Bad: Local state causing prop drilling
import React, { useState } from 'react';

// ChildComponent needs access to count and setCount, so it gets them as props
function ChildComponent({ count, setCount }) {
  return <button onClick={() => setCount(count + 1)}>Increment</button>;
}

// IntermediateComponent receives count and setCount as props but doesn't use them
function IntermediateComponent({ count, setCount }) {
  return (
    <div>
      <p>Intermediate Component</p>
      <ChildComponent count={count} setCount={setCount} />
    </div>
  );
}

// ParentComponent manages the count state and passes it down to IntermediateComponent
function ParentComponent() {
  const [count, setCount] = useState(0);

  return <IntermediateComponent count={count} setCount={setCount} />;
}

// App component renders the ParentComponent
function App() {
  return <ParentComponent />;
}

export default App;
// Good: State lifted up
import React, { useState } from 'react';

// ChildComponent receives count and setCount directly as props
function ChildComponent({ count, setCount }) {
  return <button onClick={() => setCount(count + 1)}>Increment</button>;
}

// ParentComponent manages the count state and passes it down to ChildComponent
function ParentComponent() {
  const [count, setCount] = useState(0);

  return <ChildComponent count={count} setCount={setCount} />;
}

// App component renders the ParentComponent
function App() {
  return <ParentComponent />;
}

export default App;

3. Component Lifecycle: Embrace Functional Components and Hooks

With the introduction of Hooks, functional components have become more powerful than ever. Embrace functional components and use Hooks for state management, side effects, and context. This simplifies code and avoids the complexities of class component lifecycles.

Example:

// Class component with lifecycle methods
class ExampleComponent extends React.Component {
  componentDidMount() {
    console.log('Component did mount');
  }

  componentWillUnmount() {
    console.log('Component will unmount');
  }

  render() {
    return <div>Hello, World!</div>;
  }
}
// Functional component with useEffect Hook
function ExampleComponent() {
  useEffect(() => {
    console.log('Component did mount');
    return () => console.log('Component will unmount');
  }, []);

  return <div>Hello, World!</div>;
}

4. Props: Destructuring and PropTypes

Destructuring props in functional components enhances readability. Additionally, PropTypes offer a way to document and validate component props, preventing common runtime errors.

Example:

// Destructuring props for improved readability
function Greeting({ name, age }) {
  return <div>{`Hello, ${name}! You are ${age} years old.`}</div>;
}
// PropTypes for documenting and validating props
import PropTypes from 'prop-types';

function Greeting({ name, age }) {
  return <div>{`Hello, ${name}! You are ${age} years old.`}</div>;
}

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

5. Reusable Components: Create a Component Library

Encapsulate common UI patterns into reusable components. Building a component library not only accelerates development but also ensures a consistent look and feel throughout your application.

Example:

// Reusable Button component
function Button({ onClick, children }) {
  return <button onClick={onClick}>{children}</button>;
}

6. Error Boundaries: Graceful Handling of Errors

Wrap components with error boundaries to gracefully handle errors and prevent entire UI crashes. This provides a better user experience and facilitates easier debugging.

Example:

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  componentDidCatch(error, errorInfo) {
    this.setState({ hasError: true });
    // Log error information
    console.error(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return <div>Something went wrong.</div>;
    }

    return this.props.children;
  }
}
import React, { useState } from 'react';

function ErrorBoundary({ children }) {
  const [hasError, setHasError] = useState(false);

  const componentDidCatch = (error, errorInfo) => {
    setHasError(true);
    // Log error information
    console.error(error, errorInfo);
  };

  if (hasError) {
    return <div>Something went wrong.</div>;
  }

  return children;
}

export default ErrorBoundary;

7. Performance Optimization: Memoization and PureComponent

Improve performance by memoizing components using React.memo and utilizing PureComponent for class components. This prevents unnecessary renders and optimizes your application.

Memoized Functional Component:

import React from 'react';

const MemoizedComponent = React.memo(function MyComponent(props) {
  /* render using props */
  console.log('Rendering MemoizedComponent');
  return (
    <div>
      <p>{props.text}</p>
    </div>
  );
});

export default MemoizedComponent;

PureComponent Class Component:

import React from 'react';

class MyPureComponent extends React.PureComponent {
  render() {
    /* render using this.props */
    console.log('Rendering MyPureComponent');
    return (
      <div>
        <p>{this.props.text}</p>
      </div>
    );
  }
}

export default MyPureComponent;

Usage example:

import React, { useState } from 'react';
import MemoizedComponent from './MemoizedComponent';
import MyPureComponent from './MyPureComponent';

function App() {
  const [text, setText] = useState('Initial Text');

  const handleButtonClick = () => {
    setText('Updated Text');
  };

  return (
    <div>
      <button onClick={handleButtonClick}>Update Text</button>
      <MemoizedComponent text={text} />
      <MyPureComponent text={text} />
    </div>
  );
}

export default App;

8. Code Splitting: Optimize Load Time

Break down your application into smaller chunks and load them on demand. Code splitting helps reduce initial load times, making your application more performant.

Example:

// Using React.lazy for code splitting
const MyComponent = React.lazy(() => import('./MyComponent'));

// Suspense for fallback UI while the component is loading
const App = () => (
  <React.Suspense fallback={<div>Loading...</div>}>
    <MyComponent />
  </React.Suspense>
);

9. Testing: Adopt a Robust Testing Strategy

Implement a comprehensive testing strategy using tools like Jest and React Testing Library. Write unit tests, integration tests, and end-to-end tests to ensure the reliability of your application.

Example:

// Jest and React Testing Library test
import { render, screen } from '@testing-library/react';
import MyComponent from './MyComponent';

test('renders component with correct text', () => {
  render(<MyComponent />);
  expect(screen.getByText('Hello, World!')).toBeInTheDocument();
});

10. Accessibility: Prioritize Inclusive Design

Make your application accessible to a wide range of users by following web accessibility standards. Use semantic HTML, provide proper labels, and test your application with screen readers.

Example:

// Using semantic HTML for better accessibility
<button aria-label="Close" onClick={handleClose}>
  <span aria-hidden="true">×</span>
</button>

Conclusion

Building high-quality React applications involves a combination of best practices and design patterns. By adhering to these guidelines, you’ll not only enhance the maintainability and scalability of your project but also contribute to a positive development experience for your team. Keep evolving with the React ecosystem, stay updated on new features, and continuously refine your approach to ensure your applications remain at the forefront of web development.


下载开源日报APP:https://openingsource.org/2579/
加入我们:https://openingsource.org/about/join/
关注我们:https://openingsource.org/about/love/