The mobx-state-tree examples (with and without typescript) pre-dated the next _app class and needed to be updated to use _app for persisting state across client-side navigation transitions. Also removed unneeded custom server class to better keep with the "show one feature per example" style of the next examples folder
Fixes#5352 . This updates the example updating react-i18next to v8.0.6, replacing the `translate` HOC to `withNamespaces` and `I18n` to `NamespacesConsumer`.
There is one thing that I am not sure if is correct or not so I need some guidance. You gotta wrap the page with the `withI18next` HOC so it will extend the `getInitialProps` of the page with this:
```
Extended.getInitialProps = async (ctx) => {
const composedInitialProps = ComposedComponent.getInitialProps
? await ComposedComponent.getInitialProps(ctx)
: {}
const i18nInitialProps = ctx.req
? i18n.getInitialProps(ctx.req, namespaces)
: {}
return {
...composedInitialProps,
...i18nInitialProps
}
}
```
The problem lies in `i18n.getInitialProps` that has this code:
```
i18n.getInitialProps = (req, namespaces) => {
if (!namespaces) namespaces = i18n.options.defaultNS
if (typeof namespaces === 'string') namespaces = [namespaces]
req.i18n.toJSON = () => null // do not serialize i18next instance and send to client
const initialI18nStore = {}
req.i18n.languages.forEach((l) => {
initialI18nStore[l] = {}
namespaces.forEach((ns) => {
initialI18nStore[l][ns] = (req.i18n.services.resourceStore.data[l] || {})[ns] || {}
})
})
return {
i18n: req.i18n, // use the instance on req - fixed language on request (avoid issues in race conditions with lngs of different users)
initialI18nStore,
initialLanguage: req.i18n.language
}
}
```
In my understanding, among other things, it gets the `i18n` object from the request (included by the `server.js`) and uses the data to create `initialI18nStore` and `initialLanguage`, and then return these two objects plus the `i18n` object itself. If you add the `i18n` object on the return, then there will be a crash on the client-side render of the page:
```TypeError: Cannot read property 'ready' of null```
I don't know why, but returning it breaks `NamespacesConsumer` component from `react-i18next` (the state becomes null). So I commented this line and the provider on `_app.js` is getting the `i18n` instance from the `i18n.js` file (the same as `server.js`). I don't know if this would be an issue so I would like help to debug this.
Regarding a question about having different global layouts via `_app.js` usage, it came up that we could use a static property in the page that needed a different one(Thanks @timneutkens )
Link to the Spectrum post: https://spectrum.chat/?t=af6ca794-5420-4780-abd8-96f085a19e09
This PR adds an example called `with-dynamic-app-layout`, that showcases that use case in the simplest way I could think of.
Let me know if there's changes or improvements to be made. 🎉
* Add analyze-bundles example
* housekeeping: with-webpack-bundle-analyzer example
* analyze-bundles example: revert the version of faker library
* analyze-bundles add analyze:server and analyze:browser to scripts
* with-webpack-bundle-analyzer example: fix typo
Since version 2.1, react-apollo is exposing some new components that use the function-as-child (or render-prop) pattern to let you connect apollo-client magic with your components. See the blog article: [New in React Apollo 2.1](https://www.apollographql.com/docs/react/react-apollo-migration.html)
If I'm not mistaken, it's generally agreed that this pattern is (where it works) superior to the HOC pattern, for reasons that are best explained here: https://cdb.reacttraining.com/use-a-render-prop-50de598f11ce
So I updated the with-apollo example to use the new API, and IMO this code is much simpler and natural to read and understand, especially if you are not already familiar with Apollo's HOC APIs.
I broke up my changes into separate commits, for easier review. Commits with "Refactor" in the message accomplish the goal of switching to the new APIs while minimizing line-by-line differences (select "Hide whitespace changes" under "Diff settings"). Commits with "Clean up" in the message follow up the refactoring with trivial things like reorganizing code sections, renaming variables, etc.
For the components doing mutations, I chose not to use the `Mutation` component, since that doesn't really make sense to me; a mutation is something that happens at a point in time, so it's not meaningful to represent a mutation in the markup, which exists for a period of time. All that component does is expose a `mutate` function for a single specified mutation, and `result` data for a single firing of the mutation (which we don't need anyways; apollo handles updating the local data with the result). To me it seems simpler and more flexible to just get the apollo client via `ApolloConsumer` and call `.mutate()` on it.
In case anyone is interested, here's what my version of `PostUpvoter` using the `Mutation` component looked like:
<details>
```jsx
import React from 'react'
import { Mutation } from 'react-apollo'
import { gql } from 'apollo-boost'
export default function PostUpvoter ({ votes, id }) {
return (
<Mutation mutation={upvotePost}>
{mutate => (
<button onClick={() => upvote(id, votes + 1, mutate)}>
{votes}
<style jsx>{`
button {
background-color: transparent;
border: 1px solid #e4e4e4;
color: #000;
}
button:active {
background-color: transparent;
}
button:before {
align-self: center;
border-color: transparent transparent #000000 transparent;
border-style: solid;
border-width: 0 4px 6px 4px;
content: '';
height: 0;
margin-right: 5px;
width: 0;
}
`}</style>
</button>
)}
</Mutation>
)
}
const upvotePost = gql`
mutation updatePost($id: ID!, $votes: Int) {
updatePost(id: $id, votes: $votes) {
id
__typename
votes
}
}
`
function upvote (id, votes, mutate) {
mutate({
variables: { id, votes },
optimisticResponse: {
__typename: 'Mutation',
updatePost: {
__typename: 'Post',
id,
votes
}
}
})
}
```
</details>
###
I'm happy with where things are at here, but I'm more than happy to address any comments, concerns, ideas for improvent!
Thanks!
This fixes https://github.com/zeit/next.js/issues/5260 by making sure that `index.js` has `getInitialProps` defined on the page exported component, not the child component.
When fixing that, I uncovered an issue where the server side rendered HTML did not match the clientside HTML, so I reworked _app.js to use the `i18nextprovider` component which has props to hydrate the initial data (for SSR), and makes sure the correct i18n instance is passed to all child components through context.
Before:
```html
<!DOCTYPE html>
<html>
<head>
<meta charSet="utf-8" class="next-head"/>
<link rel="preload" href="/_next/static/development/pages/index.js" as="script"/>
<link rel="preload" href="/_next/static/development/pages/_app.js" as="script"/>
<link rel="preload" href="/_next/static/development/pages/_error.js" as="script"/>
<link rel="preload" href="/_next/static/runtime/webpack.js" as="script"/>
<link rel="preload" href="/_next/static/runtime/main.js" as="script"/>
</head>
<body>
<div id="__next"></div>
<script src="/_next/static/development/dll/dll_4a2ab6ce0cb456fbfead.js"></script><script>__NEXT_DATA__ = {"props":{"pageProps":{}},"page":"/","pathname":"/","query":{},"buildId":"development"};__NEXT_LOADED_PAGES__=[];__NEXT_REGISTER_PAGE=function(r,f){__NEXT_LOADED_PAGES__.push([r, f])}</script><script async="" id="__NEXT_PAGE__/" src="/_next/static/development/pages/index.js"></script><script async="" id="__NEXT_PAGE__/_app" src="/_next/static/development/pages/_app.js"></script><script async="" id="__NEXT_PAGE__/_error" src="/_next/static/development/pages/_error.js"></script><script src="/_next/static/runtime/webpack.js" async=""></script><script src="/_next/static/runtime/main.js" async=""></script>
</body>
</html>
```
After:
```html
<!DOCTYPE html>
<html>
<head>
<meta charSet="utf-8" class="next-head"/>
<link rel="preload" href="/_next/static/development/pages/index.js" as="script"/>
<link rel="preload" href="/_next/static/development/pages/_app.js" as="script"/>
<link rel="preload" href="/_next/static/development/pages/_error.js" as="script"/>
<link rel="preload" href="/_next/static/runtime/webpack.js" as="script"/>
<link rel="preload" href="/_next/static/runtime/main.js" as="script"/>
</head>
<body>
<div id="__next">
<h1>This example integrates react-i18next for simple internationalization.</h1>
<div>
<h1>welcome to next.js</h1>
<p>This example integrates react-i18next for simple internationalization.</p>
<p>test words for en</p>
<div><button>fire in the wind for en</button></div>
<p>You can either pass t function to child components.</p>
<p>Or wrap your component using the translate hoc provided by react-i18next.</p>
<p>Alternatively, you can use <code>Trans</code> component.</p>
<a href="/page2">Go to page 2</a><br/><a href="/page3">Go to page 3 (no hoc)</a>
</div>
</div>
<script src="/_next/static/development/dll/dll_4a2ab6ce0cb456fbfead.js"></script><script>__NEXT_DATA__ = {"props":{"pageProps":{"i18n":null,"initialI18nStore":{"en":{"home":{"welcome":"welcome to next.js","sample_test":"test words for en","sample_button":"fire in the wind for en","link":{"gotoPage2":"Go to page 2","gotoPage3":"Go to page 3 (no hoc)"}},"common":{"integrates_react-i18next":"This example integrates react-i18next for simple internationalization.","pureComponent":"You can either pass t function to child components.","extendedComponent":"Or wrap your component using the translate hoc provided by react-i18next.","transComponent":"Alternatively, you can use \u003c1\u003eTrans\u003c/1\u003e component."}}},"initialLanguage":"en-US"}},"page":"/","pathname":"/","query":{},"buildId":"development"};__NEXT_LOADED_PAGES__=[];__NEXT_REGISTER_PAGE=function(r,f){__NEXT_LOADED_PAGES__.push([r, f])}</script><script async="" id="__NEXT_PAGE__/" src="/_next/static/development/pages/index.js"></script><script async="" id="__NEXT_PAGE__/_app" src="/_next/static/development/pages/_app.js"></script><script async="" id="__NEXT_PAGE__/_error" src="/_next/static/development/pages/_error.js"></script><script src="/_next/static/runtime/webpack.js" async=""></script><script src="/_next/static/runtime/main.js" async=""></script>
</body>
</html>
```
* Update .babelrc
babel-plugin-transform-decorators-legacy does not exist in babel 7 in replace use @babel/plugin-proposal-decorators
* Update package.json
Acording the last commit please, test this example , will be work .