面试题答案
一键面试1. React 16 废弃的生命周期方法
在 React 16 之后,componentWillMount
、componentWillReceiveProps
和 componentWillUpdate
被标记为不安全的生命周期方法,逐渐被废弃。原因在于 React 的异步渲染机制(Fiber)可能会导致这些方法被多次调用,从而产生不一致的状态。
2. 数据加载策略调整
- 替代
componentWillMount
:- 旧策略:在
componentWillMount
中进行数据加载,由于该方法在组件挂载前调用,此时 DOM 还未生成。 - 新策略:使用
componentDidMount
替代。此方法在组件挂载后调用,确保 DOM 已生成,并且只会调用一次,适合进行副作用操作如数据加载。例如:
- 旧策略:在
import React, { Component } from'react';
class DataLoader extends Component {
constructor(props) {
super(props);
this.state = { data: null };
}
componentDidMount() {
fetch('your-api-url')
.then(response => response.json())
.then(data => this.setState({ data }));
}
render() {
const { data } = this.state;
return (
<div>
{data? <p>{JSON.stringify(data)}</p> : <p>Loading...</p>}
</div>
);
}
}
export default DataLoader;
- 替代
componentWillReceiveProps
:- 旧策略:在
componentWillReceiveProps
中根据新的props
进行数据加载或更新状态,但其会在组件接收到新props
时就触发,无论props
是否变化。 - 新策略:使用
getDerivedStateFromProps
与componentDidUpdate
结合。getDerivedStateFromProps
是一个静态方法,在组件挂载及接收到新props
时都会被调用,用于根据props
更新state
。componentDidUpdate
则用于处理副作用,如根据新props
进行数据加载。例如:
- 旧策略:在
import React, { Component } from'react';
class DataLoader extends Component {
constructor(props) {
super(props);
this.state = { data: null };
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.id!== prevState.id) {
return { id: nextProps.id, data: null };
}
return null;
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.id!== this.props.id) {
fetch(`your-api-url/${this.props.id}`)
.then(response => response.json())
.then(data => this.setState({ data }));
}
}
render() {
const { data } = this.state;
return (
<div>
{data? <p>{JSON.stringify(data)}</p> : <p>Loading...</p>}
</div>
);
}
}
export default DataLoader;
- 替代
componentWillUpdate
:- 旧策略:
componentWillUpdate
在组件更新前调用,可用于准备更新,但同样可能因异步渲染多次调用。 - 新策略:使用
getSnapshotBeforeUpdate
与componentDidUpdate
。getSnapshotBeforeUpdate
在 DOM 更新前调用,返回值会作为componentDidUpdate
的第三个参数,可用于获取更新前的 DOM 状态等。例如:
- 旧策略:
import React, { Component } from'react';
class ScrollingList extends Component {
constructor(props) {
super(props);
this.listRef = React.createRef();
this.state = { items: [] };
}
componentDidMount() {
this.fetchData();
}
fetchData = () => {
// 模拟异步数据获取
setTimeout(() => {
this.setState({ items: [...this.state.items, 'new item'] });
}, 1000);
}
getSnapshotBeforeUpdate(prevProps, prevState) {
if (prevState.items.length!== this.state.items.length) {
const list = this.listRef.current;
return list.scrollHeight - list.scrollTop;
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (snapshot) {
const list = this.listRef.current;
list.scrollTop = list.scrollHeight - snapshot;
}
}
render() {
return (
<div ref={this.listRef}>
{this.state.items.map((item, index) => (
<p key={index}>{item}</p>
))}
<button onClick={this.fetchData}>Add Item</button>
</div>
);
}
}
export default ScrollingList;
3. 保证数据加载的正确性和性能优化(异步数据加载)
- 避免重复请求:
- 策略:在数据加载前进行检查,若数据已存在且未过期,不再重复请求。可以使用缓存机制,例如在组件内维护一个缓存变量,并设置过期时间。
- 示例:
import React, { Component } from'react';
class DataLoader extends Component {
constructor(props) {
super(props);
this.state = { data: null, cache: null, cacheTime: 0 };
}
componentDidMount() {
this.loadData();
}
loadData = () => {
const { cache, cacheTime } = this.state;
const now = Date.now();
if (cache && now - cacheTime < 60 * 1000) { // 缓存 1 分钟
this.setState({ data: cache });
return;
}
fetch('your-api-url')
.then(response => response.json())
.then(data => {
this.setState({ data, cache: data, cacheTime: now });
});
}
render() {
const { data } = this.state;
return (
<div>
{data? <p>{JSON.stringify(data)}</p> : <p>Loading...</p>}
</div>
);
}
}
export default DataLoader;
- 处理并发请求:
- 策略:使用
AbortController
来取消未完成的请求。当组件卸载或接收到新的请求条件时,取消之前的请求,防止无效请求占用资源。 - 示例:
- 策略:使用
import React, { Component } from'react';
class DataLoader extends Component {
constructor(props) {
super(props);
this.state = { data: null };
this.controller = new AbortController();
}
componentDidMount() {
this.fetchData();
}
componentWillUnmount() {
this.controller.abort();
}
fetchData = () => {
const { signal } = this.controller;
fetch('your-api-url', { signal })
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => this.setState({ data }))
.catch(error => {
if (error.name!== 'AbortError') {
console.error('Error fetching data:', error);
}
});
}
render() {
const { data } = this.state;
return (
<div>
{data? <p>{JSON.stringify(data)}</p> : <p>Loading...</p>}
</div>
);
}
}
export default DataLoader;
- 使用
Suspense
和React.lazy
(适用于 React 16.6+):- 策略:
React.lazy
用于动态导入组件,Suspense
用于处理加载状态。可以将数据加载逻辑封装在动态导入的组件中,通过Suspense
统一处理加载和错误状态,实现更优雅的异步渲染。 - 示例:
- 策略:
import React, { Suspense } from'react';
const DataComponent = React.lazy(() => import('./DataComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<DataComponent />
</Suspense>
);
}
export default App;
在 DataComponent
中进行数据加载:
import React, { useEffect, useState } from'react';
const DataComponent = () => {
const [data, setData] = useState(null);
useEffect(() => {
fetch('your-api-url')
.then(response => response.json())
.then(data => setData(data));
}, []);
if (!data) {
return null;
}
return <p>{JSON.stringify(data)}</p>;
};
export default DataComponent;