繁体   English   中英

为什么在使用 react-router 时必须手动刷新页面才能加载组件?

[英]Why do I have to manually refresh the page for the component to load when using react-router?

在此处输入图像描述 我有一个 React 应用程序,它使用 react-router-dom 从侧边栏加载不同的组件。 每当我单击侧边栏中的链接时,URL 都会发生变化,但我必须手动刷新页面以获取要加载的页面的实际内容。

我希望我的应用程序在单击侧边栏链接时自动刷新页面,而不是用户必须手动刷新页面才能加载组件。

function App() {
  const [locale, setLocale] = useState('en');

  return (
    <>
      <Routes>
        <Route path="/" element={<IntlProvider locale={locale}><Layout setLocale={setLocale} /></IntlProvider>}>
          <Route index element={<Home />} />
          <Route path="experience" element={<Experience />} />
          <Route path="skills" element={<Skills />} />
          <Route path="portfolio" element={<Portfolio />} />
          <Route path="contact" element={<Contact />} />
        </Route>
      </Routes>
    </>

  );
}

export default App;

这是我的 App.js

const Sidebar = ({ toggled, handleToggleSidebar }) => {
  const intl = useIntl();
  return (
    <ProSidebar
      toggled={toggled}
      breakPoint="md"
      onToggle={handleToggleSidebar}
    >
      <SidebarHeader>
        <div
          style={{
            padding: '24px',
            textTransform: 'uppercase',
            fontWeight: 'bold',
            fontSize: 14,
            letterSpacing: '1px',
            overflow: 'hidden',
            textOverflow: 'ellipsis',
            whiteSpace: 'nowrap',
          }}
        >
          <Link style={{ display: 'block', padding: '15px 0' }} to="/">
            <img style={{ display: 'block', margin: '-3em 3em', width: '133px', height: 'auto' }} src={LogoS} alt="Logo" />
          </Link>
        </div>
      </SidebarHeader>

      <SidebarContent>
        <Menu iconShape="circle">

          <MenuItem icon={<FaHome size={32} />}>
            {intl.formatMessage({ id: 'Home' })}
            <NavLink activeclassname="active" to="/"></NavLink>
          </MenuItem>

          <MenuItem icon={<FaProjectDiagram size={32} />}>
            {intl.formatMessage({ id: 'Experience' })}
            <NavLink to="/experience"></NavLink>
          </MenuItem>

          <MenuItem icon={<FaToolbox size={32} />}>
            {intl.formatMessage({ id: 'Skills' })}
            <NavLink to="/skills"></NavLink>
          </MenuItem>

          <MenuItem icon={<FaFolderOpen size={32} />}>
            {intl.formatMessage({ id: 'Portfolio' })}
            <NavLink to="/portfolio"></NavLink>
          </MenuItem>

          <MenuItem icon={<FaTelegramPlane size={32} />}>
            {intl.formatMessage({ id: 'Contact' })}
            <NavLink to="/contact"></NavLink>
          </MenuItem>

        </Menu>
      </SidebarContent>
    </ProSidebar >
  );
};

export default Sidebar;

这是我的 Sidebar.js

function Layout({ setLocale }) {
  const [rtl, setRtl] = useState(false);
  const [collapsed, setCollapsed] = useState(false);
  const [image, setImage] = useState(true);
  const [toggled, setToggled] = useState(false);

  const handleCollapsedChange = (checked) => {
    setCollapsed(checked);
  };

  const handleRtlChange = (checked) => {
    setRtl(checked);
    setLocale(checked ? 'ar' : 'en');
  };
  const handleImageChange = (checked) => {
    setImage(checked);
  };

  const handleToggleSidebar = (value) => {
    setToggled(value);
  };

  return (
    <div className={`app ${rtl ? 'rtl' : ''} ${toggled ? 'toggled' : ''}`}>
      <Sidebar
        image={image}
        collapsed={collapsed}
        rtl={rtl}
        toggled={toggled}
        handleToggleSidebar={handleToggleSidebar}
      />
      <Main
        image={image}
        toggled={toggled}
        collapsed={collapsed}
        rtl={rtl}
        handleToggleSidebar={handleToggleSidebar}
        handleCollapsedChange={handleCollapsedChange}
        handleRtlChange={handleRtlChange}
        handleImageChange={handleImageChange}
      />
      <Outlet />
    </div>
  );
}

export default Layout;

这是呈现页面的 Layout.js。

const Contact = () => {
  const [letterClass, setLetterClass] = useState('text-animate')
  const form = useRef()

  useEffect(() => {
    return setTimeout(() => {
      setLetterClass('text-animate-hover')
    }, 3000)
  }, [])

  const sendEmail = (e) => {
    e.preventDefault()

    emailjs
      .sendForm(
        'service_v8uv1al',
        'template_xge6tgj',
        form.current,
        'UuX3z3S-mWAnAL7BY')
      .then(
        () => {
          alert('Message successfully sent!')
          window.location.reload(false)
        },
        () => {
          alert('Failed to send the message, please try again')
        }
      )
  }

  return (
    <>
      <div className="container contact-page">
        <div className="text-zone">
          <h1>
            <AnimatedLetters
              letterClass={letterClass}
              strArray={['C', 'o', 'n', 't', 'a', 'c', 't', ' ', 'm', 'e']}
              idx={15}
            />
          </h1>
          <div className='app_footer-cards'>
            <div className='app_footer-card'>
              <img src={emailLogo} alt="email" />
              <a href="mailto:k.maumau11@gmail.com" className='p-text'>k.maumau0@gmail.com</a>
            </div>
            <div className='app_footer-card'>
              <img src={phoneLogo} alt="mobile" />
              <a href="tel: +1 (832) 764-9796" className='p-text'>+1 (832) 764-9796</a>
            </div>
          </div>
          <p>
            If you have a request or question, or simply just want to
            say Hello don't hesitate to contact me using the form below!
          </p>
          <div className="contact-form">
            <form ref={form} onSubmit={sendEmail}>
              <ul>
                <li className="half">
                  <input placeholder="Name" type="text" name="name" required />
                </li>
                <li className="half">
                  <input
                    placeholder="Email"
                    type="email"
                    name="email"
                    required
                  />
                </li>
                <li>
                  <input
                    placeholder="Subject"
                    type="text"
                    name="subject"
                    required
                  />
                </li>
                <li>
                  <textarea
                    placeholder="Message"
                    name="message"
                    required
                  ></textarea>
                </li>
                <li>
                  <input type="submit" className="flat-button" value="SEND" />
                </li>
              </ul>
            </form>
          </div>
        </div>
      </div>
      <Loader type="ball-scale" />
    </>
  )
}

export default Contact

联系页面代码

问题

离开页面时,您的应用程序代码实际上正在崩溃。 目前尚不清楚为什么您没有看到 React 错误页面(也许您正在运行生产构建),但问题是对于每个页面,您都使用useEffect挂钩来设置超时以更新某些本地状态。

useEffect(() => {
  return setTimeout(() => {
    setLetterClass('text-animate-hover');
  }, 3000);
}, []);

问题在于 React 假定从useEffect挂钩返回的任何内容都是在组件重新渲染/卸载时调用的清理函数。 setTimeout返回一个表示计时器 ID 的数字。

重新加载页面实际上是在当前 URL 路径上重新加载整个应用程序。

解决方案

在每个页面上,重构逻辑以返回清除超时的清理函数。

useEffect(() => {
  const timer = setTimeout(() => {
    setLetterClass('text-animate-hover');
  }, 3000);
  return () => clearTimeout(timer);
}, []);

这允许在组件安装时实例化超时,并且对于用户导航离开页面并且该组件卸载的偶然机会,它将清除超时并且不会调用将使状态状态排队的回调。

建议

对于AnimatedLetters组件,这些页面中的每一个似乎都具有相同的动画letterClass状态。 与其在所有页面上复制相同的代码/逻辑,不如将其抽象为一个自定义的 React 钩子,该钩子返回letterClass值。

例子:

const useLetterClass = ({
  start = 'text-animate',
  end = 'text-animate-hover'
}) => {
  const [letterClass, setLetterClass] = useState(start);

  useEffect(() => {
    const timer = setTimeout(() => {
      setLetterClass(end);
    }, 3000);
    return () => clearTimeout(timer);
  }, []);

  return { letterClass };
};

在每个页面组件中导入useLetterClass挂钩,调用并传递返回的letterClass值。

例子:

const Contact = () => {
  const { letterClass } = useLetterClass({
    start: 'text-animate',
    end: 'text-animate-hover',
  });
  const form = useRef();

  const sendEmail = (e) => {
    ...
  }

  return (
    <>
      <div className="container contact-page">
        <div className="text-zone">
          <h1>
            <AnimatedLetters
              letterClass={letterClass}
              strArray={['C', 'o', 'n', 't', 'a', 'c', 't', ' ', 'm', 'e']}
              idx={15}
            />
          </h1>
          ...
        </div>
      </div>
      <Loader type="ball-scale" />
    </>
  );
};

编辑Why-do-i-have-to-manually-refresh-the-page-for-the-component-to-load-when-using

您提供的沙箱代码似乎也有一些SCSS 的问题,特别是缺少$sidebar-color变量。 我对 SCSS 不太熟悉,但我不怀疑它与我上面描述的路由/导航问题有关。

暂无
暂无

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

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