近日在写 React 轮播图组件时遇到自动轮播的问题,在多方请教后,使用 useCallback 解决

轮播图组建需要自动轮播,我想到了使用 setTimeout 来解决 setInterval 可能越来越快的问题。心里想着:这我还不轻松拿下?一番奋战后于是就有了以下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
const Swiper: React.FC<SwiperProps> = (props) => {
const {
autoplay,
duration,
} = props;

const [currentIndex, setCurrentIndex] = useState<number>(0);
const [timer, setTimer] = useState<number>();
const childrenLength = React.Children.count(children);

function next() {
if (currentIndex > childrenLength - 2) {
setCurrentIndex(0);
} else {
setCurrentIndex(currentIndex + 1);
}
}

function prev() {
if (currentIndex <= 0) {
setCurrentIndex(childrenLength - 1);
} else {
setCurrentIndex(currentIndex - 1);
}
}

// TODO autoplay
function autoPlay() {
if (autoplay) {
Array(React.Children.count(children))
.fill(0)
.forEach(() => {
return new Promise((resolve) => {
const timerId = setTimeout(() => {
next();
}, duration);
setTimer(timerId);
resolve(true);
});
});
}
}

useEffect(() => {
autoPlay(); // error
return () => {
if (autoplay) clearTimeout(timer);
};
}, [timer, currentIndex, autoplay]);

return (
...组建内容
);
};

Swiper.defaultProps = {
...
};

export default Swiper;
报错

Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn’t have a dependency array, or one of the dependencies changes on every render

这时会报错且页面不能加载(报错详情见上文)。在我询问了群里的大佬后,得知有一个 useCallback 貌似可以解决我的问题。

于是按着大佬的思路,我做了如下改造:

使用 useCallback 将 next 和 autoPlay 进行了一次包装。

为什么要使用 useCallback 将 next 函数也进行改造呢?

警告

不使用 useCallback 将 next 进行包裹会有如下报错: Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn’t have a dependency array, or one of the dependencies changes on every render

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const next = useCallback(() => {
if (currentIndex > childrenLength - 2) {
setCurrentIndex(0);
} else {
setCurrentIndex(currentIndex + 1);
}
}
}, [currentIndex, childrenLength]);

useEffect(() => {
// autoPlay(); // error
const autoPlay = useCallback(() => {
const timer = setTimeout(() => {
next();
autoPlay();
}, duration);

return () => {
if (autoplay) clearTimeout(timer);
};
}, [timer, currentIndex, autoplay]);
}, [duration, next, autoplay]);

useEffect(() => {
if (autoplay) {
autoPlay();
}
}, [autoplay, autoPlay]);

嘿嘿,有问题不能自己死磕,还是得问问