Michal Gally
Michal Gally

Reputation: 147

React-Router-Dom - redirecting doesn't cause re-render

I'm building an online library site and every book has its own page which is set on: /script/:id/ On the book page, I'm showing recommendations for similar books, and when the user clicks one of those, it should redirect him to the book page. Although, when I use useHistory and just push the route there, the URL changes, but the component itself does not rerender with new data.

Route:

<Route exact path="/script/:id">
    <ScriptsPage getScript={(e) => getScript(e)} />
</Route>

The component where I'm calling the redirection:

const ScriptCard = ({ title, author, year, image, id }) => {
    const [ width, setWidth ] = useState(window.innerWidth);
    window.addEventListener('resize', () => setWidth(window.innerWidth));
    
    const history = useHistory();

    function redirect() {
        history.push(`/script/${id}}`);
    }

    return (
        <div className="scriptcard" onClick={() => redirect()}>
            <div className="image">
                <img src={`${path}/${image}`} />
            </div>
            <div className="content">
                <h5 className="title">{title}</h5>
                <p className="text">{author}</p>
                <p className="text">{year}</p>
            </div>
        </div>
    );
};

ScriptsPage

const ScriptsPage = ({ getScript = (f) => f }) => {
    const [ , setState ] = useState();
    const history = useHistory();
    const path = 'http://localhost:8000';
    const [ scriptInfo, setScriptInfo ] = useState();
    const id = useParams().id;

    useEffect(async () => {
        const response = await getScript(id);
        setScriptInfo(response.data.data);
    }, []);

    const redirectToViewer = () => {
        console.log('redirecting to viewer');
        history.push(`/viewer/${id}`);
    };

    return (
        <div>
            {!scriptInfo ? (
                <h1>Loading</h1>
            ) : (
                <div className="scriptspage">
                    <div>
                        // I'm displaying data here, but it has nothing to do 
                           with the question
                    </div>
                    <div className="similar-scripts">
                        <ScriptsCarousel
                            title={`Viac z kategórie ${scriptInfo.category}`}
                            id={id}
                            tag={scriptInfo.category}
                            limit={10}
                        />
                    </div>
                </div>
            )}
            <Footer />
        </div>
    );
};

Any ideas on how to solve this?

EDIT: I'm aware that there is an option to use forceRefresh on router itself, but I'd rather solve it on component level rather than having page refreshing in browser on every page redirect.

Upvotes: 0

Views: 1306

Answers (2)

Christian
Christian

Reputation: 366

I think you can use the useEffect(() => {},[]) hook here. However, you must use useState and use your useHistory() state in your useEffect.

useEffect should force a re-render whenever you update your state after pushing:

export default ScriptCard = ({ title, author, year, image, id }) => {
  const [width, setWidth] = useState(window.innerWidth);
  const [stateHistory, setStateHistory] = useState(useHistory()); // <--- here
  const [index, setIndex] = useState(0);
  window.addEventListener("resize", () => setWidth(window.innerWidth));
  let path = "test";

  useEffect(() => {
    console.log(`Successfully pushed to ${stateHistory}!`);
  }, [stateHistory]); // <--- here

  const redir = (stateHistory) => {
    stateHistory.push(`/script/${id}`); // <--- here
    setStateHistory({ ...stateHistory }); // <--- here
  };

  return (
    <div className="scriptcard" onClick={() => redir(stateHistory)}> // <!-- here -->
      <div className="image">
        <img alt="test" src={`${path}/${image}`} />
      </div>
      <div className="content">
        <h5 className="title">{title}</h5>
        <p className="text">{author}</p>
        <p className="text">{year}</p>
      </div>
    </div>
  );
}

You can also add it to ScriptsPage too.

const ScriptsPage = ({ getScript = (f) => f }) => {
    const [ , setState ] = useState();
    const [stateHistory, setStateHistory] = useState(useHistory()); // <--- here
    const path = 'http://localhost:8000';
    const [ scriptInfo, setScriptInfo ] = useState();
    const id = useParams().id;

    useEffect(async () => {
        const response = await getScript(id);
        setScriptInfo(response.data.data);
    }, []);

    useEffect(() => {
        console.log(`Successfully pushed to ${stateHistory}!`);
    }, [stateHistory]); // <--- here

    const redirectToViewer = (stateHistory) => {
        console.log('redirecting to viewer');
        stateHistory.push(`/viewer/${id}`); // <--- here
        setStateHistory({ ...stateHistory }); // <--- here
    };

    return (
        <div>
            {!scriptInfo ? (
                <h1>Loading</h1>
            ) : (
                <div className="scriptspage">
                    <div>
                        // I'm displaying data here, but it has nothing to do 
                           with the question
                    </div>
                    <div className="similar-scripts">
                        <ScriptsCarousel
                            title={`Viac z kategórie ${scriptInfo.category}`}
                            id={id}
                            tag={scriptInfo.category}
                            limit={10}
                        />
                    </div>
                </div>
            )}
            <Footer />
        </div>
    );
};

I've also added a codesandbox to help demonstrate.

Upvotes: 1

Add history.go(0) after your history.push

function redirect() {
    history.push(`/script/${id}}`);
    history.go(0)
}

Here is a codesandbox with differents usages of react-router, maybe it could help you:

https://codesandbox.io/s/react-router-with-redirect-forked-9jqjh?file=/src/root.js:460-478

Upvotes: 2

Related Questions