What is not entirely clear to me in the "with-redux" example, how would I go about having an action dispatched on server render, that consists of thunks that fetch data from some api?
Along these lines, we have the same question. For a simple blog example we were able to apply the data-fetch example to great benefit in a very straightforward manner.
But in the proto-redux branch of our same repo we are struggling very hard to get something going akin to the redux async example.
That is, involving the notification of request as an action -> api request -> notification of receive as an action "thunk" pattern.
We can see the api called, we can even see the data in a console log of the reducer... but it doesn't appear in state anywhere... we can't get a proper mapStateToProps going. Don't want to use context from the Provider cuz that's considered bad practice. Is it the only way?
Please help! (Unless this pattern is beyond what is compatible with next's approach somehow?) It would really be great for this to happen. Working on an important publishing project right now (couple of them) and would love to move everything over to Next.js!!! (love all the facilities for universal, metatag injections, breaking down the project into pages/chunks, etc.).
Thanks for a great project and platform!!
@bassbump If I understood correctly what you need to do is "wait" until you get the data you need. A way to do that is to await to a function that subscribe to the store and resolves once the last action with the data you are waiting for is executed. What getInitialProps expect is an object with data. If you have an example I can try to help.
@victorkane I don't see how are you using the store in your branch, can you point to the file where is happening?
@impronunciable First of all thanks so much for answering so promptly! I see that the branch proto-redux I pointed you to doesn't actually have our actual attempts I was describing.
In the course of the day we will update the repo with our most recent attempt, concretely, and will point you on this thread in that direction.
Thanks again!
Don't know if this is what you are interested at all, but you can do:
const mapDispatchToProps = dispatch => ({
doSomething: async param => dispatch(await callAction(param)),
});
https://github.com/relatenow/relate/blob/master/hocs/withAuth.js#L9
@bassbump here @sedubois example is perfect
I figured it out thanks @impronunciable !
If I do
export default class App extends React.Component {
static async getInitialProps ({ req }) {
const isServer = !!req
const store = initStore(reducer, null, isServer)
await store.dispatch(fetchDataFromApi())
return { initialState: store.getState(), isServer }
}
constructor (props) {
super(props)
this.store = initStore(reducer, props.initialState, props.isServer)
}
render () {
return (
<Provider store={this.store}>
<Page />
</Provider>
)
}
It will load the data before render.
But can I do this behaviour as well in a redux-connected components? This doesn't seem to work at first try.
import {connect} from 'react-redux'
class Page extends React.Component {
static async getInitialProps() {
await this.props.dispatch(fetchDataFromApi())
}
}
export default connect()(Page)
@bassbump The thing is your pages are your top-level components. You could wrap your component with a HoC that does that work on every page but in the second example you are not creating the store.
Thank you it's clear to me now
Hey @bassbump and @impronunciable,
Have you guys got a simple example of this working? (thunk / async fetch something before first render?) would be amazing if you did and could share :)
Hi, this could potentially be a separate issue, but want to make sure I'm not adding any duplicates - I'm using the with-redux (create-next-app) package. Dispatching actions to the redux store, server-side from within getInitialProps does not update the client-side redux store. More information below:
I'm creating a HOC for pages that need to fetch data, essentially with the following flow:
const getInitialPropsHOC = (Page, thunk) => {
class getInitialPropsFunc extends Component {
static async getInitialProps ({ reduxStore }) {
/*
1. Fetch data from an api endpoint
(the thunk passed into HOC) (using async/await)
2. dispatch an action to redux via `reduxStore.dispatch(action)`
with the payload from the endpoint
*/
return {} // return nothing because the `Page` that is passed into the HOC will be connected to redux store
The connected page would look something like this:
function mapStateToProps(state) {
return {
data: state.....,
};
}
const ConnectedComponent = connect(mapStateToProps)(Page)
export default getInitialPropsHOC(ConnectedComponent, thunk);
When I navigate _directly_ to the Page that is wrapped in this higher order component, I am successfully hitting the endpoint, and seem to be successfully updating redux, server-side. I've verified that by printing reduxStore.getState() and the store looks up to date with information from the API in my server terminal. However, when I log the store client side (in a componentDidMount for example), from within the connected component, the redux store is in its initial state.
However, when I navigate from another component to the Page that is wrapped in this higher order component, the data fetching happens client-side and everything works properly.
Another interesting paradigm is the fact that you can getInitialProps from within a connected component. Seems counterintuitive since getInitialProps is only available to parent pages and we often export default connect()(Component). So maybe I need to reverse my composable HOC logic?
@Schwartz10 I'm running into the same situation. It's because the with-redux HOC for nextjs creates the redux store on the server and recreates it on the client side. I'm assuming your with-redux-store HOC has this function in it:
function getOrCreateStore (initialState) {
// Always make a new store if server, otherwise state is shared between requests
if (isServer) {
return createStore(initialState)
}
// Create store if unavailable on the client and set it on the window object
if (!window[__NEXT_REDUX_STORE__]) {
window[__NEXT_REDUX_STORE__] = createStore(initialState)
}
return window[__NEXT_REDUX_STORE__]
}
When the component loads on the client, it has a different store than what was initialized on the server. Then when you switch from another component to Index, it's client-to-client rendering, so when getInitialProps runs, it's using the same state vs when it was initially server-to-client and used two separate stores.
What you could do is on the server render, get your async data and return that data as props to your component. This way your client-side will receive the data as props. Though at least for me, this is not preferable as I need them in the store. But you could dispatch the data from props on component did mount to the store, but again this still isn't preferable either but it's the only way I've found.
Your post was a while ago, did you figure out a working solution?
Hey @orpheus - this was a while back, and I'm working on a different project now, so my memory of our solution might be off.
I believe this was actually a symptom of an underlying problem we were having with serializing and deserializing the redux store with immutable-js. We needed to write some pretty gnarly serialization code to persist the store through client <-> server with immutablejs. I think we were actually having a silent error on the server side with immutable that was causing the data to be destroyed. Once we fixed that, this problem solved itself.
I see.
Every issue I’ve seen about trying to persist server side store to client side store has been solved because of some 3rd party library bug fix. I have yet to see a general working solution without any additional libraries on how to get the client side store to match the sever store.
If you have any knowledge on this or could point me to some resources it be much appreciated.
As far as I know, it’s very explicit in the ‘with-redux-store’ hoc that it creates a new store when it goes from server to client so I’m a little confused about how people are persisting it.
Thank you!
On May 11, 2019, at 9:30 AM, Jon notifications@github.com wrote:
Hey @orpheus - this was a while back, and I'm working on a different project now, so my memory of our solution might be off.
I believe this was actually a symptom of an underlying problem we were having with serializing and deserializing the redux store with immutable-js. We needed to write some pretty gnarly serialization code to persist the store through client <-> server with immutablejs. I think we were actually having a silent error on the server side with immutable that was causing the data to be destroyed. Once we fixed that, this problem solved itself.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
Most helpful comment
I figured it out thanks @impronunciable !
If I do
It will load the data before render.
But can I do this behaviour as well in a redux-connected components? This doesn't seem to work at first try.