Sky Clong
Sky Clong

Reputation: 151

Why react-router Route attr render remount not rerender when component update?

My main intention is to rewrite the Switch component, and then realize the caching of the component through the display, instead of destroying it every time.

// App
const App = () => {
  return (
    <MySwitch>
      <Route path="path1" component={<Component1>}>
      <Route path="path2" component={<Component2>}>
    </MySwitch>
  )
}

// MySwitch
const cacheRouteMap = {}
const MySwitch = props => {
  return (
    <Route 
      path="*" 
      render={
       context => {
          const location = props.location || context.location
          let element
          let match
          let currentMatchPath
          
          React.Children.forEach(props.children, child => {
          // eslint-disable-next-line no-eq-null
            if (match == null && React.isValidElement(child)) {
              element = child

              const path = child.props.path || child.props.from
              currentMatchPath = path
              match = path ? matchPath(location.pathname, {...child.props, path}) : context.match
             }
          })
        if (!cacheRouteMap[currentMatchPath]) {
           cacheRouteMap[currentMatchPath] = React.cloneElement(element, {
             location,
             computedMatch: match,
           })
         }
        return Object.values(cacheRouteMap).map(d => {
          const {path} = d.props
          return <div style={{display: path === currentMatchPath ? 'block' : 'none'}}>{d}</div>
        })
       }
      } 
    />
  )
}

The above code can run, but id the upper layer is re-render, the Route components render method inside MySwitch will remount.

Below is my test the render method re-mount every time.

react-router: 5.2.0; react-router-dom: 5.2.0;

code:

import React, {useState, useEffect} from 'react'
import {HashRouter as Router, Route, Switch, Redirect} from 'react-router-dom'

const Demo = () => {
  useEffect(() => {
    console.log('Demo did mount........')
  }, [])
  return 1111
}

const App = () => {
  const [visible, setVisible] = useState(false)
  return (
    <div>
      <div onClick={() => setVisible(!visible)}>button</div>
      <div style={{display: visible ? 'block' : 'none'}}>xxxxx</div>
      <Router>
        <Switch>
          <Route path="/" render={() => <Demo />} />
          <Redirect to="/" />
        </Switch>
      </Router>
    </div>
  )
}

when App rerender, the Demo is re mount not rerender. What should I do the Demo rerender with render function? thanks!😋

Upvotes: 0

Views: 627

Answers (2)

Nisanth Reddy
Nisanth Reddy

Reputation: 6395

Have you tried using this

<Route exact path="/"><Demo /></Route>

When you are using render={() => <Demo />}, you are basically using an anonymous function which is creating a new instance of the Demo component.

With this approach you can pass your own custom param, as well as use the hooks like useLocation given by react-router.

Sample usage

<Route exact path="/">
  <App someProp={1} />
</Route>


function App(props) {
  let location = useLocation();

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <h2>Value of someProp is {props.someProp} </h2>
      <h2>Pathname is {location.pathname}</h2>
    </div>
  );
}

Check this Code Sandbox for a working sample.

Upvotes: 1

Alixsep
Alixsep

Reputation: 400

You just need to mount the <Demo /> directly.

<Route path="/" exact>
    <Demo />
</Route>

Upvotes: 0

Related Questions