使用react-router-dom的history.push()在某些组件中工作,但在其他组件中则不然



正如标题所说。我正在使用 React-router-dom,因此在我的 App.js 文件中,我设置了包含交换机和多个路由的路由器。从几个较小的组件中,我没有问题使用 useHisory 和 history.push() 来操作历史记录和导航我的应用程序。

但是在我的应用程序.js文件中它不起作用,我得到了返回:

"类型错误: 无法读取未定义的属性'推送'">

我不知道问题是什么,任何帮助都会得到很多帮助。

import React, { useState, useEffect } from "react";
import {
BrowserRouter as Router,
Route,
Switch,
useHistory,
} from "react-router-dom";
import styled from "styled-components";
import unsplash from "../api/unsplash";
import Header from "./Header";
import Customise from "./Customise";
import LandingPage from "./LandingPage";
import GameBoard from "./GameBoard";
import GameFinished from "./GameFinished";
function App() {
const [searchImageTerm, setSearchImageTerm] = useState("south africa");
const [images, setImages] = useState([]);
const [randomisedImages, setRandomisedImages] = useState([]);
const [roundStarted, setRoundStarted] = useState(false);
const [firstSelectedTile, setFirstSelectedTile] = useState(null);
const [secondSelectedTile, setSecondSelectedTile] = useState(null);
const [matchedTiles, setMatchedTiles] = useState([]);
const [endOfTurn, setEndOfTurn] = useState(false);
const [score, setScore] = useState(0);
const [minutes, setMinutes] = useState(2);
const [seconds, setSeconds] = useState(0);
const [difficulty, setDifficulty] = useState(8);
const history = useHistory();
useEffect(() => {
getImages();
}, [searchImageTerm, difficulty]);
useEffect(() => {
randomiseImagesWithID(images);
}, [images]);
useEffect(() => {
if (minutes === 0 && seconds === 0) {
finishGame();
}
}, [seconds, minutes]);
const finishGame = () => {
history.push(`/gamefinished`);
};
useEffect(() => {
if (roundStarted) {
let myInterval = setInterval(() => {
if (seconds > 0) {
setSeconds(seconds - 1);
}
if (seconds === 0) {
if (minutes === 0) {
clearInterval(myInterval);
} else {
setMinutes(minutes - 1);
setSeconds(59);
}
}
}, 1000);
return () => {
clearInterval(myInterval);
};
}
});
useEffect(() => {
if (matchedTiles.length > 0 && matchedTiles.length === images.length / 2) {
alert("YOU WON!");
}
}, [matchedTiles]);
const getImages = async () => {
const response = await unsplash.get("/search/photos", {
params: { query: searchImageTerm, per_page: difficulty },
});
setImages(response.data.results);
};
const generateTileId = () => {
return "tile_id_" + Math.random().toString().substr(2, 8);
};
const randomiseImagesWithID = (images) => {
let duplicateImagesArray = [...images, ...images];
var m = duplicateImagesArray.length,
t,
i;
while (m) {
i = Math.floor(Math.random() * m--);
t = duplicateImagesArray[m];
duplicateImagesArray[m] = duplicateImagesArray[i];
duplicateImagesArray[i] = t;
}
let finalArray = [];
for (let image of duplicateImagesArray) {
finalArray.push({
...image,
tileId: generateTileId(),
});
}
setRandomisedImages([...finalArray]);
};
const startRound = () => {
setRoundStarted(true);
};
const onTileClick = (tileId, id) => {
// is the tile already paired && is the tile selected && is it the end of the turn?
if (
!matchedTiles.includes(id) &&
tileId !== firstSelectedTile &&
!endOfTurn
) {
// find image id for first selcted id for comparrison
const firstSelctedTileId = randomisedImages.find(
(image) => image.tileId === firstSelectedTile
)?.id;
// if there is no selected tile set first selected tile
if (!firstSelectedTile) {
setFirstSelectedTile(tileId);
} else {
// if the second tile matches the first tile set matched tiles to include
if (id === firstSelctedTileId) {
setMatchedTiles([...matchedTiles, id]);
// add points to score
setScore(score + 6);
// reset selected tiles
setFirstSelectedTile(null);
} else {
// deduct points from score
setScore(score - 2);
// set and display second tile choice
setSecondSelectedTile(tileId);
// set end of turn so tiles cannot be continued to be selected
setEndOfTurn(true);
// reset all values after a few seconds
setTimeout(() => {
setFirstSelectedTile(null);
setSecondSelectedTile(null);
setEndOfTurn(false);
}, 1500);
}
}
}
};
const onResetClick = () => {
randomiseImagesWithID(images);
setFirstSelectedTile(null);
setSecondSelectedTile(null);
setMatchedTiles([]);
setScore(0);
setEndOfTurn(false);
};
return (
<div>
<Router>
<Container>
<Header
onResetClick={onResetClick}
score={score}
seconds={seconds}
minutes={minutes}
/>
<Main>
<Switch>
<Route path="/gameboard">
<GameBoard
images={randomisedImages}
onTileClick={onTileClick}
firstSelectedTile={firstSelectedTile}
secondSelectedTile={secondSelectedTile}
matchedTiles={matchedTiles}
/>
</Route>
<Route path="/customise">
<Customise
setSearchImageTerm={setSearchImageTerm}
setDifficulty={setDifficulty}
setMinutes={setMinutes}
startRound={startRound}
/>
</Route>
<Route path="/gamefinished">
<GameFinished />
</Route>
<Route path="/">
<LandingPage startRound={startRound} />
</Route>
</Switch>
</Main>
</Container>
</Router>
</div>
);
}
export default App;
const Container = styled.div`
width: 100%;
height: 100vh;
display: grid;
grid-template-rows: 7rem;
`;
const Main = styled.div`
display: grid;
grid-template-columns: auto;
`;

并举例说明我的代码在何处按预期工作:

import React from "react";
import { useHistory } from "react-router-dom";
import styled from "styled-components";
function LandingPage({ startRound }) {
const history = useHistory();
const startGame = () => {
history.push(`/gameboard`);
startRound();
};
const customiseGame = () => {
history.push("/customise");
};
return (
<Container>
<WelcomeText>
<p>Match the tiles by picking two at a time.</p>
<p>Gain points for a correct match but lose points if they dont.</p>
<p>Good Luck!</p>
</WelcomeText>
<ButtonContainer>
<GameButton onClick={() => startGame()}>Start</GameButton>
<GameButton onClick={() => customiseGame()}>Customise</GameButton>
</ButtonContainer>
</Container>
);
}

之所以TypeError: Cannot read property 'push' of undefined,是因为您在渲染返回之前已经初始化/分配了历史记录(因此路由器从未填充过。

const history = useHistory();

将其更改为此内容,一切都应按预期工作(警告:我自己还没有测试过):

const finishGame = () => {
const history = useHistory();
history.push(`/gamefinished`);
};

它将起作用,因为finishGame仅在页面呈现后调用useEffect内部调用。

您可以将历史记录支持一个组件传递给另一个组件。

喜欢

// First component
import { useHistory } from "react-router-dom";
const firstComponent = () => {
const history = useHistory();
return (
<SecondComponent history=history />
)
}
const SecondComponent = ({history}) => (
....
);

我认为您的代码没有任何问题。试试这个

npm uninstall react-router-dom && npm i react-router-dom

然后重试。

最新更新