Support query params? #95
Description
This morning I've been playing around with how we could support query params in redux-simple-router
(just as a thought experiment).
In history.listen
we receive a Location
. With pushPath
and replacePath
we can change the API to accept LocationDescriptor
s. However, with no way of creating a LocationDescriptor
object from a LocationDescriptor
string, it's difficult to support query params.
Here's an example of the problem:
pushPath('/foo?a=b')
// store:
{ location: '/foo?a=b' }
pushPath({ pathname: '/foo', query: { a: 'b' } })
// store:
{ location: { pathname: '/foo', query: { a: 'b' } } }
history.push('/foo?a=b')
// store:
{ location: { pathname: '/foo', query: { a: 'b' } } }
<Link to='/foo?a=b'>
// store:
{ location: { pathname: '/foo', query: { a: 'b' } } }
The problem is of course that every time you pushPath
you will get the exact LocationDescriptor
you pass in into the store, but every time you history.push
or <Link>
you'll get an object. We then can't reliably support query params. (If you're very strict about always passing in a LocationDescriptorObject
you would have query support, but that feels very fragile.)
We can of course use history.createLocation
right now, but it's most likely no longer going to be public in history
V2.
To support query params we need to be able to create a LocationDescriptor
object both from a LocationDescriptor
string and from a Location
. We could then easily support query params, and we could also deepEqual
check them without knowing about query
or anything else that's actually part of the LocationDescriptor
(which is how we stop cycles, #50). With a createLocationDescriptor
(that always returns a LocationDescriptorObject
) our code base would actually be simpler, and we would get query support for free.
Okey, so some potential solutions:
Keep the solution we have now
I.e. path
and state
in store, but change to accepting LocationDescriptor
in push
and replace
. We don't support query params.
Verbatim LocationDescriptor
in store
We have to make it very clear that this is either an object or a string. So it's most likely not very usable for people. We "support" query params, iff you're very strict about always passing in a LocationDescriptorObject
. Fragile.
Always go through history
"before" store
pushPath
-> UPDATE_PATH_INTERNAL action
-> store.subscribe callback
-> history.push
-> history.listen callback
-> UPDATE_PATH action
-> store.subscribe callback where we break cycle
I.e. we don't trigger UPDATE_PATH
immediately, but only within the history.listen
callback. We would then reliably have a LocationDescriptorObject
in the store, and we could therefore reliably support query params.
This is of course a more complex solution.
Conclusion
It was interesting going through the different solutions, but with createLocation
going away and no createLocationDescriptor
on the horizon I think it's best to just keep our current solution. I don't think it's worth the added complexity.
Maybe someone else has ideas on how to solve this? Or maybe this is another case for keeping history.createLocation
public? @jlongster @mjackson @taion @justingreenberg
(Hopefully I haven't overlooked something very obvious 🙈)