Server Side Rendering with react-apollo - javascript

Best wishes, while I was using the local yoga server, and local docker container as a database, things worked very smoothly because data was loaded in split second, and thus... if someone was signed in, the name of the signed in person immediately appeared on client and server side as well.
Now that I deployed frontend, backend, and database on remote servers, it takes time to load the data. Due to this, The Sign In button stays for 3-4 seconds even if we were already signed in. And late the frontend realizes, that we were signed in and then shows our name.
This happens because we render the data only after we get the data. But when data comes late, the server-side code becomes outdated. Late updating client makes web app feel very lagging.
I am using Next.js
withData.js 👇
import withApollo from 'next-with-apollo'
import ApolloClient from 'apollo-boost'
function createClient({ headers }) {
return new ApolloClient({
uri: `${process.env.ENDPOINT}/graphql`,
request: operation => {
operation.setContext({
fetchOptions: {
credentials: 'include',
},
headers
})
}
})
}
export default withApollo(createClient);
User.js 👇
import { Query } from 'react-apollo'
import gql from 'graphql-tag'
import PropTypes from 'prop-types'
import { client } from '../lib/withData'
export const CURRENT_USER_QUERY = gql`
query {
me {
id
name
fname
lname
email
phone
bio
previledge
gender
username
birthday
profilePicture
signUpMethod
}
}
`
const User = props => (
<Query {...props} query={CURRENT_USER_QUERY}>
{payload => {
return props.children(payload)
}}
</Query>
)
export default User
SignInButton.js 👇
<User>
{({data: {me}}) => (
{ me ? <ProfileButton me={me} /> : <li style={{backgroundColor: '#ffffff', color: '#000000', borderRadius: '5px', padding: '5px 10px', zoom: '80%'}}><a href={`${meta.domain}/signin?intent=${this.props.router.asPath}`} style={{color: '#000000'}}>⚡️🚦 {this.signInText}</a></li> }
)}
</User>
pages/_app.js 👇
import App, { Container } from 'next/app'
import { ApolloProvider } from 'react-apollo'
import withData from '../src/lib/withData'
import Page from '../src/components/Page'
class Wrapper extends App {
static getInitialProps({Component, ctx}){
let pageProps = {}
if(Component.getInitialProps){
pageProps = Component.getInitialProps(ctx)
}
// This exposes query to the user
pageProps.query = ctx.query
return { pageProps }
}
render() {
const { Component, apollo, pageProps } = this.props
return (
<Container>
<ApolloProvider client={apollo}>
<Page>
<div className="super_container"><Component {...pageProps} /></div>
</Page>
</ApolloProvider>
</Container>
)
}
}
export default withData(Wrapper)
How do I render the data from react-apollo on the server side?
Found these resources but difficult to implement with the stack I use.
https://bessey.dev/blog/2019/01/02/apollo-graphql-hypernova/
https://github.com/i18next/react-i18next/issues/593
https://shaleenjain.com/blog/nextjs-apollo-prefetch/

If you want SSR by query so you can populate head and other stuff directly in serverSide, you need to make the query directly in
You need to create the query inside the GetInitialProps function like this :
Page.getInitialProps = async ({
apolloClient, query, children, router, href
}) => {
const { data, error, loading } = await apolloClient.query({ query: LIVRE_QUERY, variables: { slug: query.titre } })
if (error) {
return <div>Erreur</div>
}
return { data, error, loading }
}

Related

Remix.run Connect Ledger Web USB/HID browser module

I'm trying to get Ledger hardware wallet to connect in remix.run. I'm following this instruction
https://developers.ledger.com/docs/transport/web-hid-usb/ and I'm using remix-utils so I can use ClientOnly Component.
How do I connect to ledger hardware wallet when I Click on "Connect Ledger" button? I can't get my head around it. What I have so far is below.
ledger.client.tsx component
import "core-js/actual";
import { listen } from "#ledgerhq/logs";
import AppBtc from "#ledgerhq/hw-app-btc";
// Keep this import if you want to use a Ledger Nano S/X/S Plus with the USB protocol and delete the #ledgerhq/hw-transport-webhid import
import TransportWebUSB from "#ledgerhq/hw-transport-webusb";
export function ConnectLedgerHardWare() {
async function handleButtonClicked() {
const transport = await TransportWebUSB.create();
//listen to the events which are sent by the Ledger packages in order to debug the app
listen((log) => console.log(log));
//When the Ledger device connected it is trying to display the bitcoin address
const appBtc = new AppBtc(transport);
const { bitcoinAddress } = await appBtc.getWalletPublicKey(
"44'/0'/0'/0/0",
{ verify: false, format: "legacy" }
);
console.log(bitcoinAddress);
await appBtc.getWalletPublicKey("44'/0'/0'/0/0", {
format: "legacy",
verify: true,
});
return bitcoinAddress;
}
return (
<>
<button onClick={handleButtonClicked}>Connect ledger</button>
</>
);
}
index.tsx
import { useEffect } from "react";
import { ClientOnly, useHydrated } from "remix-utils";
import { ConnectLedgerHardWare } from "~/components/ledger.client";
export default function Index() {
const hydrated = useHydrated();
useEffect(() => {});
return (
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>
<ClientOnly fallback={<p>Loading...</p>}>
{() => <ConnectLedgerHardWare />}
</ClientOnly>
</div>
);
}
My code above is giving me this error:
Error: Cannot initialize 'routeModules'. This normally occurs when you have server code in your client modules.

Can I use getInitialProps in _app.js and in pages?

I'm working on my first serious NextJS app. I have it set up to pull in JSON data for the left nav, rather than hardcoding them in the app somewhere. This way I don't have to rebuild every time there's a minor change to the site's navigation.
Since the navigation needs to be available on every page, I added getInitialProps to the _app.js file, which grabs the left nav and passes it to the left nav component. But now as I'm moving on to build the homepage, I see that the getInitialProps there does not run. It seems that the getInitialProps in _app.js takes precendence.
Is there a way to have both? Or some other workaround that accomplishes the goal (or just a better way to do this in general)?
Note that I'm using getInitialProps for two reasons:
getStaticProps is out because I don't plan to build the entire site at build time
getServerSideProps is usually out because I don't like that it ends up doing two http requests: first a request goes to the NextJS server, then the server sends a request to my API (which happens to live on a different server). If I'm just getting basic stuff like the navigation, there's no need for getServerSideProps to run on the NextJS server, I'd rather skip the middle man
Here's some some simplified code:
_app.js:
import { Provider } from "react-redux";
import axios from "axios";
import store from "../store";
import Header from "../components/Header";
import LeftNav from "../components/LeftNav";
function MyApp(props) {
const { Component, pageProps } = props;
return (
<Provider store={store}>
<Header />
<LeftNav leftnav={props.leftnav} />
<Component { ...pageProps } />
</Provider>
)
}
MyApp.getInitialProps = async (context) => {
let config = await import("../config/config");
let response = await axios.get(`${config.default.apiEndpoint}&cAction=getLeftNav`);
if (response) {
return {
leftnav: response.data.leftNav
};
} else {
return {
leftnav: null
};
}
};
export default MyApp;
Home.js:
import axios from "axios";
const Home = (props) => {
console.log("Home props", props);
return (
<div>home</div>
);
};
Home.getInitialProps = async(context) => {
// this only runs if the getInitialProps in _app.js is removed :(
let config = await import("../config/config");
let response = await axios.get( `${config.default.apiEndpoint}&cAction=getHome` );
if ( response ) {
return {
home: response.data.home
};
} else {
return {
home: null
}
}
};
export default Home;
You have to call App.getInitialProps(context) in your _app to call the current page's getInitialProps. You can then merge the page's props with the remaining props from _app.
import App from 'next/app'
// Remaining code...
MyApp.getInitialProps = async (context) => {
const pageProps = await App.getInitialProps(context); // Retrieves page's `getInitialProps`
let config = await import("../config/config");
let response = await axios.get(`${config.default.apiEndpoint}&cAction=getLeftNav`);
return {
...pageProps,
leftnav: response?.data?.leftNav ?? null
};
};
From the custom _app documentation:
When you add getInitialProps in your custom app, you must import App from "next/app", call App.getInitialProps(appContext) inside
getInitialProps and merge the returned object into the return value.

set cookie before running fetch in getInitialProps

I have a Next.js app, I'm using getInitialProps in my _app.js in order to be able to have persistent header and footer. However, I'm also needing to set data in a Context, and I need to be able to fetch the data based off of a cookie value. I've got the basics working just fine, however my _app sets the cookie on the first load, and then when I refresh the page it pulls in the appropriate data. I'm wondering if there's a way to be able to set the cookie first before fetching the data, ensuring that, if there's a cookie present, it will always pull in that data on the first load? Here is my _app.js, and, while I'm still working on the dynamic cookie value in my cookies.set method, I'm able to fetch the right data from my Prismic repo by hard-coding sacramento-ca for now, as you'll see. All I'm really needing is the logic to ensure that the cookie sets, and then the data fetches.
_app.js
import React from 'react';
import { AppLayout } from 'components/app-layout/AppLayout';
import { Footer } from 'components/footer/Footer';
import { Header } from 'components/header/Header';
import { LocationContext } from 'contexts/Contexts';
import Cookies from 'cookies';
import { Client } from 'lib/prismic';
import NextApp, { AppProps } from 'next/app';
import 'styles/base.scss';
import { AppProvider } from 'providers/app-provider/AppProvider';
interface WithNavProps extends AppProps {
navigation: any;
location: string;
dealer?: any;
cookie: string;
}
const App = ({ Component, pageProps, navigation, dealer }: WithNavProps) => {
const { Provider: LocationProvider } = LocationContext;
const locationData = dealer ? dealer : null;
return (
<LocationProvider value={{ locationData }}>
<AppProvider>
<AppLayout>
<Header navigation={navigation} location={dealer} />
<Component {...pageProps} />
<Footer navigation={navigation} />
</AppLayout>
</AppProvider>
</LocationProvider>
);
};
export default App;
App.getInitialProps = async (appContext: any) => {
const appProps = await NextApp.getInitialProps(appContext);
const cookies = new Cookies(appContext.ctx.req, appContext.ctx.res);
try {
cookies.set('dealerLocation', 'sacramento-ca', {
httpOnly: true,
});
const { data: navigation } = await Client.getByUID('navigation', 'main-navigation', {
lang: 'en-us',
});
const results = await Client.getByUID('dealer', cookies.get('dealerLocation'), {
lang: 'en-us',
});
return {
...appProps,
navigation,
dealer: results,
};
} catch {
const { data: navigation } = await Client.getByUID('navigation', 'main-navigation', {
lang: 'en-us',
});
return {
...appProps,
navigation,
};
}
};

Email verification with Next js and express

I am building a web application in which i need to verify the user's email sent via the client side (React.js and Next.js) and i'm following this youtube tutorial. However, the mentor is using create-react-app CLI and React-Router-Dom for the routing system which doesn't really go with my current needs.
Moreover, I found this method online using HOC :
import React from 'react';
import Router from 'next/router';
const login = '/register?redirected=true'; // Define your login route address.
const checkUserAuthentication = () => {
return { auth: null }; // change null to { isAdmin: true } for test it.
};
export default WrappedComponent => {
const hocComponent = ({ ...props }) => <WrappedComponent {...props} />;
hocComponent.getInitialProps = async (context) => {
const userAuth = await checkUserAuthentication();
// Are you an authorized user or not?
if (!userAuth?.auth) {
// Handle server-side and client-side rendering.
if (context.res) {
context.res?.writeHead(302, {
Location: login,
});
context.res?.end();
} else {
Router.replace(login);
}
} else if (WrappedComponent.getInitialProps) {
const wrappedProps = await WrappedComponent.getInitialProps({...context, auth: userAuth});
return { ...wrappedProps, userAuth };
}
return { userAuth };
};
return hocComponent;
};
The code above helps me to have a private route that the user cannot access unless he's authenticated (currently no programming included), but on the other hand i still need a page in the following route :
'pages/user/activate/[token].js' // the link sent via email from express back end.
What i need now is to create this page using Next routing system in order to get the token and decode it to move forward with the back end and save the user into MongoDB, and in order to accomplish that, i have created my [token].js page with the following code :
import React, {useState, useEffect} from 'react'
import { ToastContainer, toast } from 'react-toastify';
import axios from 'axios';
import jwt from 'jsonwebtoken';
import { authenticate, isAuth } from '../helpers/auth';
import { Link, Redirect } from 'react-router-dom';
const Activate = ({ match }) => {
const [formData, setFormData] = useState({
email: '',
token: '',
show: true
});
const { email, token, show } = formData;
useEffect(() => {
let token = match.params.token;
let { email } = jwt.decode(token);
if (token) {
setFormData({ ...formData, email, token });
}
console.log(token, email);
}, [match.params.token]);
return (
<>
{isAuth() ? <Redirect to="/" /> : null}
<p>Account activated, please log in</p>
</>
)
};
export default Activate;
However, i keep getting this error :
TypeError: Cannot read property 'params' of undefined
at Activate (C:\Users\Hp\Desktop\SMP\client\.next\server\pages\user\activate\[token].js:245:13)
at processChild (C:\Users\Hp\Desktop\SMP\client\node_modules\react-dom\cjs\react-dom-
server.node.development.js:3353:14)
at resolve (C:\Users\Hp\Desktop\SMP\client\node_modules\react-dom\cjs\react-dom-
server.node.development.js:3270:5)
at ReactDOMServerRenderer.render (C:\Users\Hp\Desktop\SMP\client\node_modules\react-dom\cjs\react-
dom-server.node.development.js:3753:22)
at ReactDOMServerRenderer.read (C:\Users\Hp\Desktop\SMP\client\node_modules\react-dom\cjs\react-dom-
server.node.development.js:3690:29)
at renderToString (C:\Users\Hp\Desktop\SMP\client\node_modules\react-dom\cjs\react-dom-
server.node.development.js:4298:27)
at Object.renderPage (C:\Users\Hp\Desktop\SMP\client\node_modules\next\dist\next-
server\server\render.js:53:851)
at Function.getInitialProps (C:\Users\Hp\Desktop\SMP\client\.next\server\pages\_document.js:293:19)
at loadGetInitialProps (C:\Users\Hp\Desktop\SMP\client\node_modules\next\dist\next-
server\lib\utils.js:5:101)
at renderToHTML (C:\Users\Hp\Desktop\SMP\client\node_modules\next\dist\next-
server\server\render.js:53:1142)
I couldn't find a solution because i believe that i'm doing something wrong whether in my code or in the logic implemented.
Is there any way that i can do this properly ?
Thank you in advance !

Can't display data from REST call, even though the server is sending data

I am trying to perform a rest call with a token in the header to display information. There is a required header token so my code looks like this for my restClient.js, app.js, and users.js.
//restClient.js
import { jsonServerRestClient, fetchUtils } from 'admin-on-rest';
const httpClient = (url, options = {}) => {
if (!options.headers) {
options.headers = new Headers({ Accept: 'application/json' });
}
options.headers.set('token', 'admin');
return fetchUtils.fetchJson(url, options);
}
const restClient = jsonServerRestClient('http://localhost:8080/api/v2', httpClient);
export default (type, resource, params) => new Promise(resolve => setTimeout(() => resolve(restClient(type, resource, params)), 500));
//App.js
import React, {Component} from 'react';
import { Admin, Resource } from 'admin-on-rest';
import { UserList } from './users';
import restClient from './restClient';
class App extends Component {
render() {
return(
<Admin restClient={restClient}>
<Resource name="admin/user" list={UserList}/>
</Admin>
);
}
}
export default App;
//Users.js
// in src/users.js
import React from 'react';
import { List, Datagrid, EmailField, TextField, SimpleList } from 'admin-on-rest';
export const UserList = (props) => (
<List {...props}>
<Datagrid >
<TextField source="email"/>
<TextField source="info"/>
</Datagrid>
</List>
);
Example of JSON
I've tested my rest call with postman and it is definitely returning data. Also is there anyway to check what data is being sent back in the call? The server is running express.js and I've set up the route to include the required headers.I've also attached an example of what my JSON looks like that I am returning.
Since aor fetchUtils returns a promise. you can intercept the promise and perform any kind of inspection you want (and do a lot more too)
Below is how my code is handling something similar. I am also intercepting the API call and displaying custom notifications.
function handleRequestAndResponse(url, options={}) {
return fetchUtils.fetchJson(url, options)
.then((response) => {
const {headers, json} = response;
//admin on rest needs the {data} key
const data = {data: json}
if (headers.get('x-total-count')) {
data.total = parseInt(headers.get('x-total-count').split('/').pop(), 10)
}
// handle get_list responses
if (!isNaN(parseInt(headers.get('x-total-count'), 10))) {
return {data: json,
total: parseInt(headers.get('x-total-count').split('/').pop(), 10)}
} else {
return data
}
})
}
You can simply do something like below
return fetchUtils.fetchJson(url, options)
.then(res => {
console.log(res)
return res
})
I dont have the reputation to comment yet but Fiddler 4 is a good way to see what the UI is sending to the server.

Categories