0

我已经阅读了多个 useRef/useEffect 指令,但我似乎无法让它在这里工作。

这里的代码工作流程是:Remix/React,从数据库中获取数据,显示数据,将数据变成可以更新的代码

如果有人能指出他们在这段代码中看到的任何明显错误,比如为什么useEffect钩子没有触发,或者为什么useRef钩子永远无法在{listRef}中找到<ul>,我很想知道。

import { Links, redirect, useLoaderData, Outlet } from 'remix'
import { db } from '~/utils/db.server'
import { getUser } from '~/utils/session.server'
import { ReactSortable } from "react-sortablejs"
import { useState, useRef, useEffect } from 'react'
import tickerStylesUrl from '~/styles/tickerDisplay.css'

export const links = () => [{ rel: 'stylesheet', href: tickerStylesUrl }]

export const loader = async ({ request, params }) => {
  
  const user = await getUser(request)
  const ticker = await db.ticker.findUnique({
    where: { id: params.tickerId },
    include: {
      headlines: true,
    },
  })
  if (!ticker) throw new Error('Ticker not found')

  const data = { ticker, user }
  return data
}

export const action = async ({ request, params }) => {

}
// The ticker function displays the items without styling, so it finds the database perfectly and can get the data
function displayTicker() {
  const { ticker, user } = useLoaderData()

  const headlines = ticker.headlines
  const tickerParentStyle = {
    width: "1920px",
    height: "1080px",
    position: "relative",
    backgroundColor: "black"
  }
  const tickerStyle = {
    position: "absolute",
    padding: "0",
    bottom: "0",
    color: `${ticker.fontColor}`,
    backgroundColor: `${ticker.backgroundColor}`,
    fontFamily: `${ticker.font}`,
    fontSize: "2em",
  }
  const tickerHeadlineStyle = {
    margin: "auto",
    height: "50%",
  }
  console.log("Headlines: " + headlines)
  // So begins the found ticker code I had hoped to integrate
  // Source: https://www.w3docs.com/tools/code-editor/2123
  function scrollTicker() {

    const marquee = listRef.current.querySelectorAll('.tickerHeadlines');
    let speed = 4;
    let lastScrollPos = 0;
    let timer;
    marquee.forEach(function (el) {
      const container = el.querySelector('.headlineItem');
      const content = el.querySelector('.headlineItem > *');
      //Get total width
      const elWidth = content.offsetWidth;
      //Duplicate content
      let clone = content.cloneNode(true);
      container.appendChild(clone);
      let progress = 1;
      function loop() {
        progress = progress - speed;
        if (progress <= elWidth * -1) {
          progress = 0;
        }
        container.style.transform = 'translateX(' + progress + 'px)';
        container.style.transform += 'skewX(' + speed * 0.4 + 'deg)';
        window.requestAnimationFrame(loop);
      }
      loop();
    });
    window.addEventListener('scroll', function () {
      const maxScrollValue = 12;
      const newScrollPos = window.scrollY;
      let scrollValue = newScrollPos - lastScrollPos;
      if (scrollValue > maxScrollValue) scrollValue = maxScrollValue;
      else if (scrollValue < -maxScrollValue) scrollValue = -maxScrollValue;
      speed = scrollValue;
      clearTimeout(timer);
      timer = setTimeout(handleSpeedClear, 10);
    });
    function handleSpeedClear() {
      speed = 4;
    }
  }

  const listRef = useRef()
  console.log("listRef: " + JSON.stringify(listRef))
  // This console appears everytime, but is always empty, presumably because DOM has just rendered

  useEffect(() => {
    console.log("useEffect fired")
    // This console NEVER fires, sadly. I thought this would happen ONCE rendered
  }, [listRef]);

  return (
    <>
      <Links />
      <div style={tickerParentStyle}>
        <div style={tickerStyle}>
          <div key={ticker.id} style={tickerHeadlineStyle} class="tickerWrapper">
// HERE IS THE TARGET UL
            <ul className="tickerHeadlines" ref={listRef} style={{ margin: "10px 0 10px 0" }} >
              {/* Hoping to map through the ticker items here, and have them displayed in a list, which would then be manipulated by the useRef/useEffect hook */}
              {headlines.map((headline) => (
                <>
                  <li class="headlineItem" key={headline.id}>
                    <span>
                      {headline.content} {ticker.seperator}
                    </span>
                  </li>
                </>
              ))}
              {scrollTicker()}
            </ul>
          </div>
        </div>
      </div>
    </>
  )
}

export default displayTicker

一如既往,任何帮助表示赞赏。

4

2 回答 2

2

useRef是一个用于访问 DOM 元素的钩子,直接在 React 应用程序中操作 DOM 打破了声明式编程的全部要点。完全不建议直接使用任何 dom 对象和方法来操作 DOM,例如document. 来到useEffect钩子,useEffect钩子根据依赖数组中提供的内容有条件地运行,如果没有,钩子只在组件完成安装后运行一次。因此,您应该注意需要传递给useEffect依赖数组的内容。考虑到您的情况,当您通过时listRefuseEffect仅当对象而不是其属性发生更改时才运行,因为对象是非原始的,属性的任何更改都不会被视为对象的更改,它只是一个不会导致重新的对象属性突变-使成为。为了明确起见,您应该确定,正如您所提到的,您希望它在何时被调用,您希望它在数据呈现后立即运行,您可以改为headlines在依赖项数组中使用。将依赖数组更改为包含headlines

 useEffect(() => {
    console.log("useEffect fired")
    // This console NEVER fires, sadly. I thought this would happen ONCE rendered
  }, [headlines]);

或者,您也可以将其留空,使其仅在组件安装后运行一次。

useEffect(() => {
    console.log("useEffect fired")
    // This console NEVER fires, sadly. I thought this would happen ONCE rendered
  }, []);

需要注意的是,每次更改时,前一个片段都会运行headlines,而无论发生什么变化,后者只会运行一次。因此,根据您的用例,您可能希望选择最适合您需求的一种。

于 2022-01-29T19:11:13.233 回答
0

有几件事可以让代码变得更好:

  1. 使用“null”值启动 ref

  2. 在 useEffect Hook 中调用你的 'scrollTicker' 函数。

  3. 组件卸载时始终删除侦听器。关注https://reactjs.org/docs/hooks-reference.html#useeffect了解更多详情

  4. 你可以像这样使用 useEffect 钩子:

    useEffect(() => { // 在这里使用你的 ref return () => { // 移除 linteners

    }; });

于 2022-02-02T20:44:30.310 回答