I'm facing a problem where I need to fetch data when I enter different routes
Here's an example with just a single view that needs to fetch data. In reality I will have multiple views, and model.items will change on different views.
I'm not sure how to achieve this functionality without onRoute hooks.
const { h, app } = hyperapp
/** @jsx h */
app({
model: {
items: []
},
reducers: {
updateItems: (model, data) => ({
items: data
})
},
effects: {
fetch: (model, actions) => {
setTimeout(function () {
actions.updateItems([1, 2, 3])
},0)
}
},
view: (model, actions) => {
/* I need to asynchronously fetch items only one time when I enter routes.
If I call an effect here in the view, it will rerender infinitely
since reducers update the model and cause views to render
*/
actions.fetch()
console.log('this is logged on every render')
return (
<div>{model.items}</div>
)
}
})
Sounds reasonable and something we will be able to achieve _without_ any hacks after https://github.com/hyperapp/hyperapp/issues/120.
@traducer With regards to this issue. I have a question. How are you switching views?
User clicks a link/button? What about fetching the view's data before you switch views?
You could create a function switchView(url, actions) that fetches some data and then calls actions.router.go(url) or some other combination.
Does I make sense?
@jbucaran hmm, the problem I'm having looks something like this:
-> user navigates to path /items
-> the view function for this route calls actions.fetchItems()
-> items get fetched from the server and updates model.items
-> view rerenders on model change
the problem is everytime the view is rendered, actions.fetchItems is being called, which is creating an infinite loop since new items are updating the model. I want to be able to call actions.fetchItems only once when someone navigates to that route.
Switching views your way works perfectly fine. A click event would only call actions.fetchItems once and actions.router.go(url) would change routes. But what if they refresh the page or manually type in the pathname in their browser address bar? How would that data be fetched?
I hope this makes sense.
@traducer
``javascript
app({
actions: {
fetch (model, actions) =>
console.log(fetching ${model.router.params.id}`)
,
customGo: (model, actions, path) => {
actions.router.go(path)
actions.fetch()
}
},
subscriptions: [
(model, actions) => actions.fetch()
]
})
@dodekeract This actually looks like a nice solution to my problem.
I didn't think of using subscriptions to fetch the initial data. I guess I can just check the location.pathname in the subscriptions as well to fetch data depending on the pathname?
```javascript
subscriptions: [
(model, actions) => {
switch (location.pathname) {
case '/items':
actions.fetchItems()
return
}
}
````
This actually clears things up for me. Thanks @dodekeract
@traducer In theory the router should do that for you. It stores everything you need in model.router.match and model.router.params.
That also has the advantage that you don't have to manually match /item/:id. (model.router.params.id)
@dodekeract Ah I see now. I was still using version 0.5 so I didn't see those changes.
@dodekeract Thanks for figuring out!
@traducer Do you want to close?
Everything is cleared up, thanks!
Most helpful comment
Everything is cleared up, thanks!