50个开发人员都应该知道的 React.js 超级技巧(上)

React.js 是一个用于构建用户界面的流行 JavaScript 库,它提供了丰富的功能来增强你的开发工作流程。

这篇文章,我们将以问题和解决方案的形式呈现了 50 个基本技巧、提示和窍门,并进行了详细说明,以帮助您充分利用 React。

但是因为文章篇幅有限,我们将近50个基本技巧拆开了为上下两篇文章,每篇文章分享25个技巧,今天这篇就是50个技巧的前25个内容。

那么,我们现在就开始吧。

1. 组件可重用性

问题:

您需要在应用程序中创建多个外观和行为相似的按钮。反复编写相同的按钮代码会增加出现错误的风险,并使您的代码更难维护。

解决方案:

创建一个可重用的按钮组件。此组件可以在整个应用程序中使用不同的标签和点击处理程序。通过将按钮逻辑封装在一个地方,您可以确保一致性和易于维护。

```jsx
const Button = ({ label, onClick }) => (
<button onClick={onClick}>{label}</button>
);
// Usage
<Button label="Save" onClick={handleSave} />
<Button label="Cancel" onClick={handleCancel} />
```

2. 默认属性

问题:

有时组件可能无法收到所有必要的属性,这可能是由于疏忽或应用程序的性质。您希望确保即使缺少某些属性,组件也能正常运行。

解决方案:

使用“defaultProps”定义属性的默认值。这可确保您的组件具有合理的默认值,并防止在未提供某些属性时组件崩溃。

```jsx
const Button = ({ label, onClick }) => (
<button onClick={onClick}>{label}</button>
);
Button.defaultProps = {
label: 'Click me',
};
// Usage
<Button onClick={handleClick} /> // Renders a button with label "Click me"
```

3. Prop 类型

问题:

您想确保传递给组件的 props 类型正确。不正确的 prop 类型可能会导致意外行为和难以追踪的错误。

解决方案:

使用“PropTypes”强制执行 props 的类型检查。如果 props 与预期类型不匹配,则通过在控制台中提供警告来帮助在开发过程的早期发现错误。

```jsx
import PropTypes from 'prop-types';
const Button = ({ label, onClick }) => (
<button onClick={onClick}>{label}</button>
);
Button.propTypes = {
label: PropTypes.string,
onClick: PropTypes.func.isRequired,
};


// Usage
<Button label="Save" onClick={handleSave} /> // No warnings
<Button label={42} onClick={handleSave} /> // Warning: Failed prop type: Invalid prop `label` of type `number` supplied to `Button`, expected `string`.
```

4. 带钩子的函数式组件

问题:

类组件的编写可能冗长且繁琐,尤其是对于只需要状态管理或生命周期方法的简单组件。

解决方案:

使用带钩子(如 `useState` 和 `useEffect`)的函数式组件。函数式组件更简单、更易读,钩子提供了一种管理状态和副作用的强大方法。

```jsx
import React, { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
};
// Usage
<Counter />
```

5. 自定义钩子

问题:

您有一些需要在多个组件间重复使用的状态逻辑。复制粘贴逻辑会导致代码重复,并使维护变得困难。

解决方案:

创建自定义钩子以封装可重复使用的逻辑。自定义钩子允许您轻松地在不同组件间抽象和重用状态逻辑。

```jsx
import { useState, useEffect } from 'react';
const useFetch = (url) => {
const [data, setData] = useState(null);
useEffect(() => {
fetch(url)
.then((response) => response.json())
.then((data) => setData(data));
}, [url]);
return data;
};


// Usage
const App = () => {
const data = useFetch('https://api.example.com/data');
return <div>{data ? data.title : 'Loading…'}</div>;
};
```

6. Context API

问题:

Prop 钻取使得通过许多嵌套组件传递数据变得很麻烦。这使得代码更难维护和理解,尤其是在大型应用程序中。

解决方案:

使用 Context API 通过组件树传递数据而无需进行 prop 钻取。Context 提供了一种在组件之间共享主题、用户信息等值的方法,而无需明确地通过树的每一层传递 prop。

```jsx
import React, { createContext, useContext } from 'react';
const ThemeContext = createContext('light');
const ThemeProvider = ({ children }) => {
return (
<ThemeContext.Provider value="dark">{children}</ThemeContext.Provider>
);
};
const ThemedComponent = () => {
const theme = useContext(ThemeContext);
return <div className={`theme-${theme}`}>Themed Component</div>;
};


// Usage
<ThemeProvider>
<ThemedComponent />
</ThemeProvider>
```

7. 错误边界

问题:

如果应用程序的某个部分发生错误,则会导致整个应用程序崩溃,使用户难以继续使用未受影响的部分。

解决方案:

使用错误边界捕获组件树中任何位置的 JavaScript 错误,记录这些错误并显示后备 UI,而不是使整个应用程序崩溃。错误边界会在渲染期间、生命周期方法中以及其下方的整个树的构造函数中捕获错误。

```jsx
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// Log the error to an error reporting service
console.log(error, errorInfo);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}


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

8. 记忆化

问题:

当组件的 props 或状态未发生改变时,组件会不必要地重新渲染,从而导致性能问题。

解决方案:

使用 `React.memo` 来防止不必要的重新渲染。`React.memo` 是一个高阶组件,它会记忆组件,仅在其 props 发生改变时才重新渲染。

```jsx
const MyComponent = React.memo(({ value }) => {
return <div>{value}</div>;
});


// Usage
<MyComponent value="Hello" /> // Only re-renders if `value` changes
```

9. useCallback 和 useMemo

问题:

渲染中的内联函数和复杂计算可能会导致不必要的重新渲染或性能瓶颈。

解决方案:

使用 `useCallback` 来记忆函数,使用 `useMemo` 来记忆值,防止在每次渲染时重新创建它们,除非它们的依赖项发生变化。

```jsx
import React, { useCallback, useMemo } from 'react';
const computeExpensiveValue = (a, b) => {
// expensive calculation
return a + b;
};
const MyComponent = ({ a, b }) => {
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
const memoizedCallback = useCallback(() => {
console.log(memoizedValue);
}, [memoizedValue]);
return (
<div>
<button onClick={memoizedCallback}>Compute</button>
<div>{memoizedValue}</div>
</div>
);
};


// Usage
<MyComponent a={1} b={2} />
```

10. 延迟加载

问题:

一次性加载大型组件会减慢应用程序的初始渲染速度。

解决方案:

使用 React 的 `lazy` 和 `Suspense` 实现组件的代码拆分和延迟加载。这样,组件仅在需要时才加载,从而缩短应用程序的初始加载时间。

```jsx
import React, { lazy, Suspense } from 'react';
const LazyComponent = lazy(() => import('./LazyComponent'));
const App = () => (
<Suspense fallback={<div>Loading…</div>}>
<LazyComponent />
</Suspense>
);


// Usage
<App />
```

11. Fragments

问题:

您需要从组件返回多个元素,而无需向 DOM 添加额外节点。

解决方案:

使用 React Fragments 对多个元素进行分组,而无需添加额外节点。这对于从组件返回子元素列表非常有用,而无需将它们包装在不必要的父元素中。

```jsx
const List = () => (
<>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</>
);
// Usage
<ul>
<List />
</ul>
```

12. 列表中的键

问题:

渲染元素列表时,React 需要一种方法来识别哪些项目已更改、已添加或已删除。如果没有唯一键,React 就无法有效优化渲染。

解决方案:

渲染列表时始终提供唯一键,以帮助 React 识别哪些项目已更改。键在兄弟元素之间应该是唯一的,并且是稳定的(不会随时间而变化)。

```jsx
const List = ({ items }) => (
<ul>
{items.map((item) => (
<li key={item.id}>{item.name}</li
>
))}
</ul>
);
// Usage
const items = [{ id: 1, name: 'Item 1' }, { id: 2, name: 'Item 2' }];
<List items={items} />
```

13. 受控组件

问题:

您需要管理 React 中表单输入的状态,确保表单数据始终与组件的状态同步。

解决方案:

使用受控组件,其中表单数据由 React 组件中的状态处理。这样,输入的值由组件的状态驱动,确保一致性和控制力。

```jsx
const Form = () => {
const [value, setValue] = useState('');
const handleChange = (event) => {
setValue(event.target.value);
};
return (
<input value={value} onChange={handleChange} />
);
};
// Usage
<Form />
```

14. 非受控组件

问题:

对于简单的表单,管理每个输入的状态可能很麻烦且没有必要。

解决方案:

对于不需要处理每个输入状态的简单表单,请使用带有 ref 的非受控组件。非受控组件依赖 DOM 来管理表单数据。

```jsx
const UncontrolledForm = () => {
const inputRef = React.createRef();
const handleSubmit = (event) => {
event.preventDefault();
alert(inputRef.current.value);
};
return (
<form onSubmit={handleSubmit}>
<input type="text" ref={inputRef} />
<button type="submit">Submit</button>
</form>
);
};
// Usage
<UncontrolledForm />
```

15. 合成事件

问题:

由于事件处理不一致,以跨浏览器方式处理用户交互可能具有挑战性。

解决方案:

React 使用合成事件以一致的跨浏览器方式处理用户交互。合成事件使不同浏览器的行为标准化,从而使您的事件处理代码更加可靠。

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


// Usage
<Button />

16. 批量更新

问题:

快速连续多次更新状态会导致多次重新渲染,从而导致性能问题。

解决方案:

React 批量更新状态以优化渲染。当状态依赖于先前的值时,使用功能更新以确保使用最新状态。

```jsx
const Counter = () => {
const [count, setCount] = useState(0);
const increment = () => {
setCount((prevCount) => prevCount + 1);
};
return (
<div>
<button onClick={increment}>Increment</button>
<p>{count}</p>
</div>
);
};
// Usage
<Counter />
```

17. Refs

问题:

有时您需要直接访问 DOM 节点或 React 元素来与其交互(例如,用于焦点管理或动画)。

解决方案:

使用 refs 直接访问 DOM 节点或 React 元素。可以使用 `React.createRef()` 或 `useRef` 钩子创建 Refs。

```jsx
const FocusInput = () => {
const inputRef = React.createRef();
const handleClick = () => {
inputRef.current.focus();
};
return (
<div>
<input type="text" ref={inputRef} />
<button onClick={handleClick}>Focus the input</button>
</div>
);
};
// Usage
<FocusInput />

18. Portals

问题:

您需要渲染父 DOM 层次结构之外的组件,例如模态框或工具提示。

解决方案:

使用 React Portals 将子项渲染到父组件 DOM 层次结构之外的 DOM 节点中。

```jsx
import ReactDOM from 'react-dom';
const Modal = ({ children }) => {
return ReactDOM.createPortal(
<div className="modal">
{children}
</div>,
document.getElementById('modal-root')
);
};


// Usage
<Modal>
<h1>Modal Content</h1>
</Modal>
```

19. 高阶组件 (HOC)

问题:

您需要重用组件逻辑,而无需重复代码。

解决方案:

使用高阶组件 (HOC) 创建可为现有组件添加额外功能的组件。

```jsx
const withLogging = (WrappedComponent) => {
return class extends React.Component {
componentDidMount() {
console.log('Component mounted');
}
render() {
return <WrappedComponent {…this.props} />;
}
};
};


// Usage
const MyComponent = () => <div>Hello</div>;
const MyComponentWithLogging = withLogging(MyComponent);
<MyComponentWithLogging />

20. 受控组件与非受控组件

问题:

管理表单输入可能很棘手,您需要在受控组件和非受控组件之间做出选择。

解决方案:

当您需要处理组件状态中的表单数据时,请使用受控组件;当您想要更简单的表单时,请使用非受控组件。

```jsx
// Controlled Component
const ControlledInput = () => {
const [value, setValue] = useState('');
return <input value={value} onChange={(e) => setValue(e.target.value)} />;
};
// Uncontrolled Component
const UncontrolledInput = () => {
const inputRef = useRef();
return <input ref={inputRef} />;
};
```

21. 组件组合

问题:

您需要从较简单的组件构建复杂的组件,以提高代码的重用性和可读性。

解决方案:

使用组件组合通过组合较简单的组件来构建复杂的组件。

```jsx
const Header = () => <header>Header</header>;
const Content = () => <main>Content</main>;
const Footer = () => <footer>Footer</footer>;
const Layout = () => (
<div>
<Header />
<Content />
<Footer />
</div>
);


// Usage
<Layout />
```

22. 动态导入

问题:

您想动态加载组件以缩短应用程序的初始加载时间。

解决方案:

仅在需要时使用动态导入来加载组件。

```jsx
import React, { lazy, Suspense } from 'react';
const LazyComponent = lazy(() => import('./LazyComponent'));
const App = () => (
<Suspense fallback={<div>Loading…</div>}>
<LazyComponent />
</Suspense>
);


// Usage
<App />
``

23. 带有 useReducer 的 Context

问题:

跨多个组件管理复杂的状态逻辑可能很困难。

解决方案:

将 `useReducer` 与 Context API 结合起来,可以更有效地管理复杂的状态逻辑。

```jsx
const initialState = { count: 0 };
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();
}
}
const CountContext = React.createContext();
const CountProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<CountContext.Provider value={{ state, dispatch }}>
{children}
</CountContext.Provider>
);
};
const Counter = () => {
const { state, dispatch } = useContext(CountContext);
return (
<div>
<p>{state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
</div>
);
};


// Usage
<CountProvider>
<Counter />
</CountProvider>
```

24. 转发 Refs

问题:

您需要通过组件将 ref 传递给其子组件之一。

解决方案:

使用 `React.forwardRef` 将 ref 转发给子组件。

```jsx
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="fancy-button">
{props.children}
</button>
));


// Usage
const ref = React.createRef();
<FancyButton ref={ref}>Click me</FancyButton>
```

25. 使用 Refs 进行焦点管理

问题:

您需要以编程方式管理焦点,例如在组件安装时聚焦输入字段。

解决方案:

使用 refs 以编程方式管理焦点。

```jsx
const FocusInput = () => {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus();
}, []);
return <input ref={inputRef} />;
};
// Usage
<FocusInput />
```

总结

以上就是我今天跟你分享的前面25个React的知识技巧,希望这些技巧能够帮助到你,如果有什么问题,欢迎在留言区给我留言,我们一起学习交流,加油。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值