简易手写react-router(函数式组件)

自己实现一个react-router

期望最简单的使用效果

export default () => {
      return (
        <>
            菜单a
            菜单b
        
        
            
            
        
      )
}
Router一般分HashRouter和BrowserHistoryRouter,原理区别分别是对location.hash和history api的处理

先写一些公用组件
// Link
const Link = ({ children }) => children

// Route 
const Route = () => {}

实现HashRouter

原理是在Link上绑定点击更改hash值事件,利用浏览器监听hash值的变化做相应的动作(渲染相应组件),因为是函数组件,会用到useEffect,useState

// HashRouter
const HashRouter = ({ children }) => {
  const [hash, setHash] = useState(window.location.hash)
  // hash变更触发
  const hashChange = e => {
      const { newURL } = e
      setHash(newURL.slice(newURL.indexOf('#')))
  }
  // 记得销毁时要清除绑定
  useEffect(() => {
    window.addEventListener('hashchange', hashChange)
    return window.removeEventListener('hashchange', hashChange)
  }, [])
 // 为什么不用children遍历,因为children有3种情况(1. 没子组件会返回undefined 2.单个子组件返回一个对象 3.多个子组件返回数组),而React.Children会自行判断,比较安全,当然要多个null兜底,因为返回undefined也会报错
  return React.Children.map(children, child => {
      if(child.type.name === 'Route') {
        if(child.props.path === hash) {
            return child.props.children
        }
        return null
      }
      return child.props.children
  }) || null
}

相应Link也要添加点击逻辑

const Link = ({ to, children, handleClick }) => {
    const handleClick = () => window.location.hash = to
    {children}
}
加一点排版

最简单的功能就这样完成了

...

接着搞嵌套路由看看

export default () => 

const C1 = () => 
我是C1
const C2 = () =>
我是C2
const C = () => (
C1组件 C2组件

c下面的组件

)
#/c的效果
点击C1

点击C1或C2的Link,发现整个C都消失了,分析到因为当前hash应当是#/c/c1,而C组件本身对于要在#/c的时候才会渲染,用全等判断自然不会渲染出来,子组件C1和C2肯定也不会出来


hash全等

将全等改成includes,发现效果出来了

    if (child.type.name === 'Route') {
      if (hash.includes(child.props.path)) {
        return child.props.children
      }
      return null
    }
改动后

待续更新BrowserRouter

你可能感兴趣的:(简易手写react-router(函数式组件))