简体   繁体   English

反应钩子 useState() 的非常奇怪的行为

[英]Really weird behavior of the react hook useState()

I am currently writing a react app using Swiper for a simple slideshow and everything works fine.我目前正在使用 Swiper 编写一个用于简单幻灯片放映的 React 应用程序,一切正常。 I am now trying to improve the navigation by displaying the current slide with the setActiveSlide function.我现在试图通过使用 setActiveSlide 函数显示当前幻灯片来改进导航。 After the first loading, it works fine.第一次加载后,它工作正常。 After clicking the buttons that call setActiveSlide, the story object is set to null, which results in an error (Uncaught TypeError: Cannot read property 'slideNext' of null) when you click the Previous or Next buttons a second time.单击调用 setActiveSlide 的按钮后,故事对象被设置为 null,这会导致在您第二次单击“上一个”或“下一个”按钮时出现错误(Uncaught TypeError: Cannot read property 'slideNext' of null)。 Why is the story object set to null after calling setActiveSlide?为什么调用setActiveSlide后story对象设置为null? Please help请帮忙

Here is a simplified version of the app这是应用程序的简化版本

import React, { useState, useEffect, useRef } from "react";
import Swiper from "swiper";

const Slideshow = props => {
    var swiper = useRef();
    var story = null;
    const [activeSlide, setActiveSlide] = useState("");

    useEffect(() => {
        story = new Swiper(swiper.current, {
            loop: false,
            speed: 1100
        });
    }, []);

    const prevSlide = () => {
        setActiveSlide("PREV");
        story.slidePrev();
    };

    const nextSlide = () => {
        setActiveSlide("NEXT");
        story.slideNext();
    };

    return (
        <div className="container-story" ref={swiper}>
            <div className="container-story-wrapper swiper-wrapper">
                {props.children}
            </div>

            <div className="navigation">
                <button onClick={prevSlide}> Previous </button>
                <button onClick={nextSlide}> Next </button>

                {activeSlide}
            </div>
            <div className="lightbox-container"></div>
        </div>
    );
};
export default Slideshow; 

This is a function component, so if you want a variable to persist through multiple renders then you cannot declare it locally like you do with var story = null .这是一个函数组件,因此如果您希望一个变量在多次渲染中保持不变,那么您不能像使用var story = null那样在本地声明它。 Your first render will have it set but when the component re-renders you set it back to null.您的第一个渲染将设置它,但是当组件重新渲染时,您将其设置回 null。

The proper way to deal with this would be to use Reacts useRef hook.处理这个问题的正确方法是使用useRef钩子。 This is a way to have a variable persist through multiple renders.这是一种让变量在多次渲染中保持不变的方法。

To declare it: const _story = useRef(null);声明它: const _story = useRef(null);

To read/write to the _story variable you will need to access _story.current .要读取/写入_story变量,您需要访问_story.current So in your useEffect mount you should use this instead:所以在你的useEffect挂载中你应该使用这个:

_story.current = new Swiper(swiper.current, {
    loop: false,
    speed: 1100
});

And in your handlers: _story.current.slideNext();在你的处理程序中: _story.current.slideNext();

You can read more about useRef here .您可以在此处阅读有关useRef更多信息。

So the problem is所以问题是

Assignments to the 'story' variable from inside React Hook useEffect will be lost after each render.每次渲染后,React Hook useEffect 内部对“story”变量的赋值将丢失。 To preserve the value over time, store it in a useRef Hook and keep the mutable value in the '.current' property.要随着时间的推移保留该值,请将其存储在 useRef Hook 中,并将可变值保留在 '.current' 属性中。 Otherwise, you can move this variable directly inside useEffect.否则,您可以直接在 useEffect 中移动此变量。

So you need to update as follow所以你需要更新如下

import React, { useState, useEffect, useRef } from "react";
import "./styles.css";

import Swiper from "swiper";

const Slideshow = props => {
  var swiper = useRef();
  var story = useRef(null);
  const [activeSlide, setActiveSlide] = useState("");

  useEffect(() => {
    story.current = new Swiper(swiper.current, {
      loop: false,
      speed: 1100
    });
  }, []);

  const prevSlide = () => {
    setActiveSlide("PREV");
    story.current.slidePrev();
  };

  const nextSlide = () => {
    setActiveSlide("NEXT");
    story.current.slideNext();
  };

  return (
    <div className="container-story" ref={swiper}>
      <div className="container-story-wrapper swiper-wrapper">
        {props.children}
      </div>

      <div className="navigation">
        <button onClick={prevSlide}> Previous </button>
        <button onClick={nextSlide}> Next </button>

        {activeSlide}
      </div>
      <div className="lightbox-container" />
    </div>
  );
};
export default Slideshow;

Working codesandbox工作代码沙盒

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM