Module federation - SSR data fetching on remote module - reactjs

The problem: I am trying to set up a simple module-federation enabled React application that uses server-side rendering.
There is a host application hostServer. It consumes a remote component called SharedComponent, which is served by another application called remoteServer.
I want the SharedComponent to do some data fetching and have the results be part of the initial server render.
The code: I am starting with Zack Jackson's SSR example here:
https://github.com/module-federation/module-federation-examples/tree/master/server-side-render-only
It does all I need minus the data fetching.
What I've tried: Modifying the SharedComponent.js along the lines of the following - which I know won't work, but I couldn't think of anything else.
import React from 'react';
import fetch from 'node-fetch';
const SharedComponent = () => {
const [data, setData] = React.useState([])
async function getData(){
const data = await fetch('https://swapi.dev/api/planets')
const json = await data.json();
setData(json)
}
React.useEffect(() => {
getData()
}, [])
return (
<>
<div>Shared Component2222</div>
<p>Data; {JSON.stringify(data)}</p>
</>
);
}
export default SharedComponent;
Result: As expected, the data fetching happens client side rather than server side.
How can I get SharedComponent to do server-side data fetching?

Related

How do I make Next.js 13 server-side components in the app directory that depend on useEffect for props?

I'm trying to write a Next.js 13 newsletter page in the app directory that uses server-side components that depend on useEffect for props. The useEffect fetches data from a REST API to get newsletters which will render the content of the page. The code I'm using is below. I'm having trouble figuring out how to configure the server-side components to work when I need to "use client" for interactivity. How can I make sure that the server-side components are rendered before it is sent to the client?
Code:
import Navbar from '#/components/navbar'
import Footer from '#/components/footer'
import Pagination from './pagination'
import IssueCards from './issueCards';
import { useState, useEffect } from 'react';
import axios from 'axios';
const Newsletters = () => {
const [issues, setIssues] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [issuesPerPage, setIssuesPerPage] = useState(5);
useEffect(() => {
const fetchIssue = async () => {
const res = await axios.get(`${process.env.NEXT_PUBLIC_BACKEND_API}/newsletters`)
setIssues(res.data)
}
fetchIssue()
}, [])
// Change page
const paginate = (pageNumber) => setCurrentPage(pageNumber);
const indexOfLastIssue = currentPage * issuesPerPage;
const indexOfFirstIssue = indexOfLastIssue - issuesPerPage;
const currentIssues = issues.slice(indexOfFirstIssue, indexOfLastIssue)
return (
<>
<Navbar />
<div className="newsletter-container" id='newsletter-container'>
<h1>Newsletters</h1>
<hr></hr>
<div className="newsletter-wrapper">
<IssueCards issues={currentIssues} />
<Pagination
issuesPerPage={issuesPerPage}
totalIssues={issues.length}
paginate={paginate}
/>
</div>
</div>
<Footer />
</>
);
}
export default Newsletters;
How do I configure Next.js 13 server-side components that depend on useEffect for props and ensure that the content is rendered before it is sent to the client?
I tried following the Nextjs docs on Server and Client components but I am unsure of how I can pass down the props information onto the server.
Unfortunately, server components don't allow for hooks such as useEffect, see documentation here.
You have two main options:
New way of fetching data
Server components allow for a new way of fetching data in a component, described here.
This approach would look something this:
async function getData() {
const res = await fetch('https://api.example.com/...');
// The return value is *not* serialized
// You can return Date, Map, Set, etc.
// Recommendation: handle errors
if (!res.ok) {
// This will activate the closest `error.js` Error Boundary
throw new Error('Failed to fetch data');
}
return res.json();
}
export default async function Page() {
const data = await getData();
return <main></main>;
}
Revert to client components
Your other option is to use the use client directive at the top of your file and leaving Newsletter as a client component. Of course, this way, you wouldn't get the benefits of server components, but this would prevent you from having to change your code substantially. Also, keep in mind that server components are still in beta.

React Query does not work with Astro Framework

I'm trying to use React-Query with Astro to fetch data from my Django Rest Framework backend. Astro has been a great way to organize my react-based frontend but I am worried it might not be compatible with React-Query.
Whenever I try to make a query to my backend I get an 'isLoading' value of true (and an isError of false). I never manage to recover the data from my endpoints however.
I have been following a variety of tutorials with the same results. Here is the code where I'm stuck:
import { QueryClient, useQueryClient, QueryClientProvider, useQuery } from '#tanstack/react-query';
import { gettestApi } from "../../api/testApi";
function MyComponent(props) {
const queryClient = useQueryClient();
const {
isLoading,
isError,
error,
data: test
} = useQuery('test', gettestApi)
let content
if (isLoading) {
content = <p>Loading...</p>
} else if (isError){
content = <p>{error.message}</p>
} else {
content = JSON.stringify(test)
}
As you can see, I import an axios function from /api/testAPI.js which looks like this:
import axios from "axios"
const testApi = axios.create({
baseURL: "http://127.0.0.1:8000"
})
export const gettestApi = async () => {
return await testApi.get("/api/endpoint/").then(response => response.data)
}
That's how most tutorials I have seen and the official documentation wrap up their examples, however my backend server which should be triggered by this endpoint records absolutely no hits from react-query, which is curious to me. I understand that nothing 'calls' my react-query or my gettestApi() function, but it seems to be unnecessary for other people to retrieve their data.
Maybe it would be useful to point out that contrary to other framework with React, Astro does not have an App.js root to surround with
<QueryClientProvider client={client}>
<App />
</QueryClientProvider>
Instead, I have added these QueryClientProvider brackets to the highest React component I could.
I feel like I'm missing some intuition about Tanstack Query/ React-Query. Could anybody point me in the right direction? Thanks a lot for the help.
From what I've seen in the astro docs:
The most important thing to know about Astro components is that they render to HTML during your build. Even if you run JavaScript code inside of your components, it will all run ahead of time, stripped from the final page that you send to your users. The result is a faster site, with zero JavaScript footprint added by default.
So it seems all react code only runs on the server, where data fetching via useEffect or useSyncExternalStore subscriptions just doesn't run. But this is exactly what react-query is doing, so yeah I think they don't work well together. I'm also not sure what the purpose of react-query in a setting would be where there is no client side javascript.
You can set client:only on your Astro component so the React component doesn't run on the server. There are shared-state limitations but still React Query feels better than just fetch + useEffect + own-code even if its not in a complete React app. In this example I'm also using an init function that reads cookies from the client's browser which is another case for when to use client:only.
Astro:
---
import Layout from "../../layouts/Layout.astro";
import ClientPanel from "../../components/client/ClientPanel";
---
<Layout title={ 'Client' }>
<ClientPanel client:only></ClientPanel>
</Layout>
React:
// imports
const queryClient = new QueryClient()
/** client:only component */
const ClientPanel = () => (
<QueryClientProvider client={queryClient}>
<ClientData />
</QueryClientProvider>
)
const ClientData = () => {
const { getUser, getSession } = useSession(); // read cookies functions
const [ user, setUser ] = useState(getUser);
const { isLoading, error, data } = useQuery({
queryKey: ['patientData'],
queryFn: () => getSession() // validate or refresh token
.then(session => fetchPatientData(session.tokens.token))
.catch(error => error === 'INVALID_SESSION' ? null : undefined)
})
if (!user || data === null) window.location.replace('/login')
// return statement, etc.

How to delay rendering in React Native or make rendering happen after the data is found

Image of Code
So I had a question about how to run rendering in react native after data is loaded from a database. In my code, I want to list prescriptions, but it keeps giving errors as it tries to load the prescription in the rendering function before executing the code that reaches out to firebase and gets the prescription data. How do I make it so that the rendering happens after the firebase data is gathered.
The easiest way to do this is to just have a loading state in React, this can default to true and once the data has been retrieved from Firebase you set to false. Then in your jsx you can return a loader or similar while the loading state is true and only render the rest of the screen that relies on the Firebase data once it's available and loading is set to false. Here's a minimal demo of this concept:
https://codesandbox.io/s/great-shockley-i6khsm?file=/src/App.js
import { ActivityIndicator, Text } from "react-native";
const App = () => {
const [loading, setLoading] = useState(true);
useEffect(() => {
// This is where you would have your Firebase function.
setTimeout(() => setLoading(false), 5000);
}, []);
if (loading) {
return <ActivityIndicator />;
}
return <Text>This is my app showing Firebase data</Text>;
};
export default App;
If you want to read a bit further and handle a potential error state if the Firebase function fails then here's a neat article to avoid an anti-pattern having a loading, success and error state. https://dev.to/tehaisperlis/the-loading-anti-pattern-7jj
If you look at the documentation here (https://rnfirebase.io/firestore/usage)
this is there get() example
import firestore from '#react-native-firebase/firestore';
const users = await firestore().collection('Users').get();
const user = await firestore().collection('Users').doc('ABC').get();
This means that you have to get() this data through async/await so do this below
useEffect(()=>{
const fetch = async ()=>{
/*
Your get code here
*/
const users = await firestore().collection('Users').get();
}
// Call fetch
fetch();
},[])

Axios get request from local server url from within React app

I have a Typescript Express server with an API route that also serves a Typescript React app on the domain root.
E.g.
https://myapp.test returns the React Web App
https://myapp.test/api/message returns some message string
Inside of the React app, I'm using axios to sent a GET request to the API route and obtain the message:
function App() {
const [data, setData] = useState("");
useEffect( () => {
( async () => {
const message = (await axios("http://localhost:8000/api/message")).data as string;
setData(message);
})();
}, []);
return (
<div>Message is: {data}</div>
);
}
Can I supply the request URL as one relative to the current URL? For example, something like "/api/message". If not, how should I be configuring React so that it can reliably know the API url, regardless of where it is deployed (e.g. Heroku)?
const message = (await
axios(window.location.host+"/api/message")).data as string;

intergation of flask for ML into a meteor.js app

I have build a simple pure react and meteor web app. I am trying to connect a flask API to my meteor.js app for the machine learning component of my application. I have seen examples for pure react front end but cant get the same logic to work for meteor.
what I did is:
make a flask app and return the prediction results to localhost:5000 as a python dictionary e.g.
{'class': 'bird', 'confidence':'0.8932'}
Set up a proxy in my meteor app in package.json, I have meteor app running at localhost:3000:
"proxy":"http://127.0.0.1:5000/"
finally, this is where I am confused, I have a bunch of components in my home page, I am not sure if I have to render the flask results in a component or page, nor how to do that. What I tried Is to render the results in one of the components using the useEffect, useState functions.
I get an error that says something like I can't use this funtionality.
function App() {
const [predictedClass, setPredictedClass] = useState(0);
useEffect(() => {
fetch('/prediction').then(res => res.json()).then(data => {
setPredictedClass(data.class);
});
}, []);
I use the useEffect method to get the data from the requests of my api.
example:
const [data, setData] = useState(null);
useEffect(() => {
getData('GET', '/api/test')
.then(response => {
setData(response)
})
.catch(error =>
console.log(error)
);
}, []);
Where getData is a custom function that calls an axios request.

Resources