react router (react-router-dom) setting page title from current route (functional components)?
Asked Answered
S

4

7

I thought this would be easy, but still can't find an easy solution. I simply want to set the page title (and document.title) to a specified string that I choose. I know I can access useLocation and get the current route, but that doesn't work with logical human naming. For example, my route might be /new but I want the title of the page to be 'Add New Thing'. Notice that I've added a new prop for the page title that seems like a logical place to add it (then use a useEffect to pull it), but can't figure out how to access this prop.

If this isn't the correct way, how would you do this? Should I instead setup a dictionary/lookup for the current url(useLocation) and assign a human page title?

Main goal: If someone DIRECTLY hits a URL '/new' how would you set the title?

My current structure is from a base create react app.

In my index.js

ReactDOM.render(
    <React.StrictMode>
      <Router>
        <App />
      </Router>
    </React.StrictMode>,
    document.getElementById('root')
);

app.js

<div className="quiz-app-row">
        <SideBarNav />
        <div className='quiz-app-col'>
          <h1>{TITLE HEREEEEE}</h1>
          <Switch>
            <Route exact component={Home} path="/" title='home' />
            <Route exact component={Example} path="/manage" title='example' />
            <Route exact component={CreateQuiz} path="/new" title='New Quiz' />
          </Switch>
        </div>
      </div>
Snowmobile answered 13/1, 2021 at 15:38 Comment(2)
Didn't you want to replace the <h1>TITLE HEREEEEE</h1> with dynamic title?Existence
@TaghiKhavari yes, but since I had no way yet of accessing the title prop of <Route> I didn't even show it. I'll edit it just in case others are confused.Snowmobile
S
6

Thanks all for the responses, but I feel most of them seem to be overkill for what I'm trying to accomplish, and no extra package needed. Instead, I just created a lookup collection and assigned the title that way. I will only have ~7 links total, so this seems manageable.

const [pageTitle, setPageTitle] = useState('Home');

  const titleMap = [
    {path: '/', title:'Home'},
    {path: '/manage', title:'Manage'},
    {path: '/new', title:'New Quiz'}
  ]

  let curLoc = useLocation();
  useEffect(() => {
    const curTitle = titleMap.find(item => item.path === curLoc.pathname)
    if(curTitle && curTitle.title){
      setPageTitle(curTitle.title)
      document.title = curTitle.title
    }
  }, [curLoc])
Snowmobile answered 13/1, 2021 at 20:12 Comment(1)
Just a review comment, your solution which works fine however it is using imperative approach (ie. updating the document manually) whereas using react-helmet is the declarative approachOvenware
C
3

Use react-helmet to set the title dynamically

Install it by using

npm install --save react-helmet

Now when you installed react-helmet, you can use it by simply putting this fragment anywhere

import {Helmet} from "react-helmet";
...
<Helmet>
    <title>My title</title>
</Helmet>

Example Approach

You can create your own route variant in a different file

import React from "react";
import {Helmet} from "react-helmet";

const RouteWithTitle = ({ component: Component, title, ...rest}) => (
   <Route {...rest} render={(props)=> (
       <Helmet>
           <title>{title}</title>
       </Helmet>
       <Component {...routeProps} />
   )} />
)

I hope this wll help you.

Coplin answered 13/1, 2021 at 15:55 Comment(0)
E
2

I think the best way to do what you're trying to accomplish based on your App Component is to use a Context for your title like this:

first create Title Context like this:

const TitleContext = React.createContext();
const useTitle = () => React.useContext(TitleContext);

const TitleProvider = ({ children }) => {
  const [title, setTitle] = useState("THIS IS DEFAULT TITLE");

  return (
    <TitleContext.Provider value={{ title, setTitle }}>
      {children}
    </TitleContext.Provider>
  );
};

Then you need to change your App Component a bit

const App = () => {
  const { title } = useTitle();

  return (
    <div className="quiz-app-row">
      <SideBarNav />
      <div className="quiz-app-col">
        <h1>{title}</h1>
        <Switch>
          <Route exact component={Home} path="/" title="home" />
          <Route exact component={Example} path="/manage" title="example" />
          <Route exact component={CreateQuiz} path="/new" title="New Quiz" />
        </Switch>
      </div>
    </div>
  );
};

also wrap your App Component in TitleProvider like this:

ReactDOM.render(
  <React.StrictMode>
    <TitleProvider>
      <Router>
        <App />
      </Router>
    </TitleProvider>
  </React.StrictMode>,
  document.getElementById("root")
);

and finally you can set the title in your components like this

const Home = () => {
  const { setTitle } = useTitle();

  React.useEffect(() => {
    setTitle('This is human readable title for Home Component')
  }, [])

  return <div>I'm Home Component</div>;
};
Existence answered 13/1, 2021 at 15:58 Comment(0)
S
1

use document.title = "Add New Thing"; on your CreateQuiz component. im not sure if its the best way. But it works for me

Scincoid answered 13/1, 2021 at 15:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.