似乎触发了useEffect,即使依赖状态未改变?
P粉946336138
P粉946336138 2023-08-15 16:21:30
0
1
342
<p>我正在尝试在我的React项目中构建一个轮播组件 - 实际上,我试图利用一个用纯JS编写的现有轮播组件,使用Refs在React中使其工作,而不是直接修改DOM。当我尝试通过将序列中的第一张幻灯片标记为当前幻灯片(具有className current-slide)来初始化轮播时,我遇到了问题。我将此操作放在了一个useEffect钩子中,其中{topMovies}是依赖项。{topMovies}是一个通过prop传递给轮播组件的对象数组,因此当topMovies发生变化(即加载)时,useEffect应该运行。这个操作是有效的 - 加载后,轮播中的第一张幻灯片被赋予了classname 'current-slide'。</p> <p>然而,第二段代码(nextSlide和moveToSlide)是选择下一张幻灯片的函数的开始。将track Ref(即所有幻灯片的容器)传递给函数,以便可以选择具有className 'current-slide'的子元素,并删除其className。这也是有效的 - 但是在短暂的延迟(约1秒)后,className 'current-slide'重新出现。</p> <pre class="brush:php;toolbar:false;">const Carouselv2 = ({topMovies}) =&gt; { //Refs const trackRef = useRef(); const nextBtnRef = useRef(); const prevBtnRef = useRef(); const navDotsRef = useRef(); //Initialise carousel upon prop load const [isLoading, setIsLoading] = useState(true); useEffect(() =&gt; { if (topMovies &amp;&amp; setIsLoading) { const slides = Array.from(trackRef.current.children); slides[0].classList.add('current-slide'); //Mark first slide as visible const slideWidth = slides[0].getBoundingClientRect().width; const setSlidePosition = (slide, index) =&gt; { slide.style.left = slideWidth * index + 'px'; } slides.forEach(setSlidePosition); //Arrange slides alongside each other setIsLoading(false); } }, [topMovies]); //Button functionality const moveToSlide = (currentSlide, targetSlide) =&gt; { //trackRef.current.style.transform = 'translateX(-' + targetSlide.style.left + ')'; currentSlide.classList.remove('current-slide'); //targetSlide.classList.add('current-slide'); } const updateDots = () =&gt; { } const responsiveBtns = () =&gt; { } const prevSlide = (e) =&gt; { } const nextSlide = (e) =&gt; { const currentSlide = trackRef.current.querySelector('.current-slide'); const nextSlide = trackRef.current.children.nextElementSibling; moveToSlide(currentSlide, nextSlide); } return ( &lt;div className="carousel"&gt; &lt;button className="carousel_button carousel_button--left" ref={prevBtnRef}&gt;&lt;FontAwesomeIcon icon={faCircleLeft} size="xl" style={{ color: "white" }} /&gt;&lt;/button&gt; &lt;div className="carousel_track-container"&gt; &lt;ul className="carousel_track" ref={trackRef}&gt; {topMovies &amp;&amp; topMovies.map((movie) =&gt; ( &lt;li className="carousel_slide"&gt; &lt;img src={movie.image} alt="" /&gt; &lt;h2&gt;{movie.title}&lt;/h2&gt; &lt;/li&gt; ))} &lt;/ul&gt; &lt;/div&gt; &lt;button onClick={(e) =&gt; nextSlide(e)}className="carousel_button carousel_button--right" ref={nextBtnRef}&gt;&lt;FontAwesomeIcon icon={faCircleRight} size="xl" style={{ color: "white" }} /&gt;&lt;/button&gt; &lt;div className="carousel_nav" ref={navDotsRef}&gt; &lt;button className="carousel_indicator current-slide"&gt;&lt;/button&gt; {topMovies &amp;&amp; Array.apply(0, Array(topMovies.length)).map(() =&gt; { return &lt;button className="carousel_indicator"&gt;&lt;/button&gt; })} &lt;/div&gt; &lt;/div&gt; ); }</pre> <p>我最初的想法是由于某种原因,更改className导致useEffect触发,因此我添加了带有isLoading条件的if语句。但这并没有起作用,所以我不认为bug来自于useEffect钩子。</p> <p>对此感到非常困惑,但这是我第一次使用Refs,所以完全有可能我错过了一些非常明显的东西。非常感谢您的帮助!</p>
P粉946336138
P粉946336138

全部回复(1)
P粉642436282

您正在混合使用React创建的DOM和使用纯Javascript操作的DOM。您不能这样做,这只会破坏DOM / VDOM关系。

如果您想避免将整个组件移动以按照React的方式工作,那么只需在空元素上使用Ref,好处是您可以获得组件挂载和卸载的好处。

然后,您几乎可以复制粘贴Javascript代码,只需将其全部放在useEffect中即可。

例如:

下面我创建了两个按钮,一个完全由React控制,另一个由JS控制,当您点击它时,它只会切换pink-button类。 我还放置了一个复选框来翻转按钮的顺序以引起重新渲染,以展示JS版本的状态没有被破坏,请确保在这样做时使用key属性让React知道它是相同的组件。这里的主要观点是展示React现在将不再干预这个按钮,它现在完全由您负责。希望这能帮助您了解如何将React与纯JS混合使用。

const domContainer = document.querySelector('#mount');
const root = ReactDOM.createRoot(domContainer);

const {createRef, useEffect, useState} = React;

function JSButton() {
  const btnRef = createRef(); 
  useEffect(() => {
     //do everything now from inside this useEffect
     const btn = btnRef.current;     
     btn.innerText = 'This is a Javascript Button';     
     const doClick = () => btn.classList.toggle('pink-button');
     btn.addEventListener('click', doClick);     
     return () => {
       //we can also do any clean-up here
       btn.removeEventListner('click', doClick);
     }
  }, []);
  //only render the button component,
  //don't add any props, classes etc.
  //our JS will be handling it all.
  return <button ref={btnRef}/>;
}

function ReactButton() {
  const [pink, setPink] = useState(false);
  return <button 
    onClick={() => setPink(p => !p)}
    className={pink ? 'pink-button' : ''}>
    React Button
  </button>;
}

function Test() {
  const [jsFirst, setJsFirst] = useState(false);
  return <React.Fragment>
     <input type="checkbox"
       checked={jsFirst}
       onChange={(e) => {
         setJsFirst(e.target.checked);
       }}
     />Js First
     {jsFirst 
       ? <React.Fragment>
           <JSButton key={'js'}/>
           <ReactButton key={'r'}/>
         </React.Fragment>
       : <React.Fragment>
           <ReactButton key={'r'}/>
           <JSButton key={'js'}/>
         </React.Fragment>       
     }
  </React.Fragment>
}

root.render(<Test/>);
.pink-button {
  background-color: pink;
}

button {
  display: block;
  width: 200px;
  margin: 1rem;
}
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>

<div id="mount"></div>
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责声明 Sitemap
PHP中文网:公益在线PHP培训,帮助PHP学习者快速成长!