add Route Changing reactive source?
Closed this issue · 10 comments
do we have such status, so we can check this status to prevent watching further watchpathchange reactive source that call react setState?
when u work with ui framework like react, you build a tracker container no matter a react class componet or functional component with useeffect hook, you start a tracker in it and you track flowrouter's reactive source like watchpathchange, getparams etc, then after you invoke flowrouter.go, this will lead container's tracker rerun Immediately then call react setState, but meanwhile this container will recieve undefine props from route state and pass down to child component will cause crash before next flowrouter's action call a reactdom.render.
i will post a repo to reproduce.
and wonder how people solve such problem in normal?
useEffect(function () {
const comp = Tracker.autorun(function () {
const keyword = flowrouter.getQuery('keyword')
if (!keyword) return // i want to replace here to watch if route is not changing
setState(keyword)
})
return function () {
comp?.stop()
}
}, [])
Hello @crapthings,
It's solved either with wrapping reactive code into Tracker.nonreactive
or accessing non-reactive state FlowRouter._current.route
. And checking if state returns undefined props to avoid any further action.
Maybe @afrokick can tell you more, I know they are using FR with react a lot
Yeah, we had the same issue. We decided to use Tracker.nonreactive.
//our custom hook
const useParam = pName => Tracker.nonreactive(() => FlowRouter.getParam(pName));
function MyPage() {
const someNonReactiveParam = useParam('p');
...
}
If we want to handle params changes, just pass it to component via route definition in action function. Another solution to use custom hook like useReactiveParam.
function MyPage2({ someReactiveParam }) {
...
}
FlowRouter.route('/somepath', {
action({ pName }) {
render(<MyPage2 someReactiveParam={pName}/>);
},
https://github.com/crapthings/fr88
{path: "/", params: {…}, route: Route, context: Context, oldRoute: Route, …} undefined
page2 unmount
home mount
{path: "/", params: {…}, route: Route, context: Context, oldRoute: Route, …} undefined
i'm about to leave page2 to the home page, and you can see that an tracker rerun happend and it tracks the next route state.
yes i do use pass props to react component, but what i try to achieve is something like
the container don't use react context to pass down deep props, it accesses route state directly with tracker
something like this
export default function () {
useEffect(function () {
console.log('page2 mount')
const trackerHandler = Tracker.autorun(function () {
if (FlowRouter.changingRoute()) return
console.log(FlowRouter.current(), FlowRouter.getQueryParam('something'))
})
return function () {
console.log('page2 unmount')
trackerHandler?.stop()
}
}, [])
return (
<div>page2</div>
)
}
meteor account has such reactive source that know a state of login
export default function () {
useEffect(function () {
console.log('home mount')
const trackerHandler = Tracker.nonreactive(function () {
console.log(FlowRouter.current(), FlowRouter.getQueryParam('something'))
})
return function () {
console.log('home unmount')
trackerHandler?.stop()
}
}, [])
return (
<div>home</div>
)
}
FlowRouter.setQueryParams({ something: 111111 })
FlowRouter.go('/page1?something=111111122222')
but these methods will failed if we stay same page
it looks useLayoutEffect works
export default function () {
// const mounted = useRef(false)
useLayoutEffect(function () {
console.log('page1 mount')
const trackerHandler = Tracker.nonreactive(function () {
return Tracker.autorun(function () {
// if (mounted.current === false) return
console.log('page1 inside tracker', FlowRouter._current, FlowRouter.getQueryParam('something'))
})
})
return function () {
// mounted.current = false
console.log('page1 unmount')
trackerHandler?.stop()
}
}, [])
return (
<div>page1</div>
)
}
If you want to handle query changes, use action params:
export default function (props) {
console.log(`props:`, props);
useEffect(function () {
console.log('page1 mount')
return function () {
console.log('page1 unmount')
}
}, [])
return (
<div>page1</div>
)
}
FlowRouter.route('/page1', {
action (_,q) {
render(<Layout><Page1 something={q.something} /></Layout>, ReactRoot)
}
})
@afrokick i do use these in some case, but what i want is i don't use props pass down, or react createContext provider, i want to use Meteor's reactive source, and make an react wrapper component to subscribe state from reactive dict, var, minimongo etc...
thanks useLayoutEffect works like componentDidMount after i read react document again.
the issue only happen in hook component.