首页 > 解决方案 > Pushstate 和 popstate 不适用于条件渲染的组件

问题描述

我有条件地渲染组件(屏幕),因为用户将被引导通过不同的屏幕。我正在尝试实现一种功能,用户可以使用浏览器的后退和前进按钮在这些不同的屏幕或组件之间导航。我正在努力实现这一点。任何建议将不胜感激。

沙盒链接: https ://codesandbox.io/s/history-push-and-pop-state-listening-forked-1keiz?file=/src/App.js

      const [show1, setShow1] = useState(true);
      const [show2, setShow2] = useState(false);
      const [show3, setShow3] = useState(false);

      let stateForHistory = {
       show1: false,
       show2: false,
       show3: false
     };

      const handleClick = () => {
       setShow1(false);
       setShow2(true);

      if(show2)
       setShow2(false)
       setShow3(true)
    };

      //saving state onmount
      useEffect(() => {
       window.history.pushState(stateForHistory, "", "");
     }, []);

     useEffect(() => {
      window.addEventListener("popstate", (e) => {
      let { show1, show2, show3 } = e.state || {};
      if (!show1 && show2) {
        setShow1(true);
        setShow2(false);
      }
    });
    return () => {
      window.removeEventListener("popstate", null);
    };
  });

  return (
    <div className="App">
      <div  id="screen-1">
        {show1 && <Screen1 />}
     </div>
     <div  id="screen-2">
       {show2 && <Screen2 />}
     </div>
     <div id="screen-3">
       {show3 && <Screen3 />}
     </div>

  <button onClick={handleClick} id="btn">
    Next
  </button>
</div>
  );
}

标签: javascriptreactjs

解决方案


我建议使用路由包来处理导航方面,您的代码可以专注于屏幕。您所描述的听起来类似于步进器。

指数

将其包裹App在路由器中。

import ReactDOM from "react-dom";
import { BrowserRouter as Router } from "react-router-dom";
import App from "./App";

const rootElement = document.getElementById("root");
ReactDOM.render(
  <Router>
    <App />
  </Router>,
  rootElement
);

应用程序

使用路径参数定义路由以匹配“步骤”或屏幕。

function App() {
  return (
    <div className="App">
      <Switch>
        <Route path="/step/:step">
          <Stepper />
        </Route>
        <Redirect to="/step/1" />
      </Switch>
    </div>
  );
}

创建一个屏幕步进器组件来监听路由的变化,特别是step参数的变化,并有条件地渲染屏幕。

const ScreenStepper = () => {
  const history = useHistory();
  const { step } = useParams();
  const { path } = useRouteMatch();

  const nextStep = (next) => () =>
    history.push(generatePath(path, { step: Number(step) + next }));

  const renderStep = (step) => {
    switch (step) {
      case 3:
        return <Screen3 />;

      case 2:
        return <Screen2 />;

      case 1:
        return <Screen1 />;
      default:
    }
  };

  return (
    <>
      <h1>Step: {step}</h1>
      {renderStep(Number(step))}
      <button disabled={Number(step) === 1} onClick={nextStep(-1)} id="btn">
        Previous
      </button>
      <button onClick={nextStep(1)} id="btn">
        Next
      </button>
    </>
  );
};

您可以对此进行扩展/自定义以限制屏幕数量,或从数组中渲染屏幕路线等...

编辑 pushstate-and-popstate-not-working-with-condtionally-rendered-components


推荐阅读