preactjs / preact-router

:earth_americas: URL router for Preact.
http://npm.im/preact-router
MIT License
1.01k stars 156 forks source link

Router unmounts Routes on parent re-render #409

Closed orangecoloured closed 2 years ago

orangecoloured commented 2 years ago

I have this state in my root component which is passed then as a context value

export const GlobalStateContext = createContext({
    stateGlobal: stateGlobalInitial,
    setStateGlobal: (_state: Partial<AppStateProps>) => { return },
})

const [stateGlobal, setStateGlobal] = useReducer<AppStateProps, Partial<AppStateProps>>(
    (state, newState) => ({ ...state, ...newState }),
    stateGlobalInitial,
)

const contextValue = useMemo(
    () => ({ stateGlobal, setStateGlobal }),
    [stateGlobal]
)

<GlobalStateContext.Provider value={contextValue}>
    <Menu />
</GlobalStateContext.Provider>

On each update components inside the <Router> get re-mounted. Components sitting outside of the <Router> act normal.

const A: FunctionalComponent = () => {
    useEffect(() => {
        console.log('MOUNTED')

        return (): void => {
            console.log('UNMOUNTED')
        }
    }, [])

    return <div>yo</div>
}

<div id="root">
    <GlobalStateContext.Provider value={contextValue}>
        <Menu />
    </GlobalStateContext.Provider>
    <div>
        <Router>
            <Route path="/" component={A} />
        </Router>
    </div>
</div>

So in the above example on each contextValue update you get UNMOUNTED in the console. Is there a way to fix this behaviour? Or am I doing something wrong?

developit commented 2 years ago

It seems like you're creating a new A function on every render. Components should never be created within another component or render method, since that breaks referential equality and causes them to remount on every update.

Does not work Works
```js function App() { const A: FunctionalComponent = () => { useEffect(() => { console.log('MOUNTED') return (): void => { console.log('UNMOUNTED') } }, []) return
yo
} return (
); } ```
```js const A: FunctionalComponent = () => { useEffect(() => { console.log('MOUNTED') return (): void => { console.log('UNMOUNTED') } }, []) return
yo
} function App() { return (
); } ```
orangecoloured commented 2 years ago

@developit I think you are right. I've already updated my code to avoid this re-mount, but probably the previous logic was re-creating the component. Thank you.