Meteor / React SSR and data withTracker - reactjs

I am trying to devellop a SSR app using meteor and react. Everything went pretty fine until I got the following error message :
Warning: Did not expect server HTML to contain a <div> in <div>.
which is apparently due to a difference between the tree created on server and the one on client.
I have the following to get and then show the data :
class Index extends Component {
renderCategories() {
return this.props.categories.map((category) => {
return <div key={category._id}>{category.name}</div>
})
}
render() {
if(!this.props.ready){
return null
}
return (
<div className="index">
<div className="index__right">
{this.props.categories ? this.renderCategories() : <div></div>}
</div>
</div>
)
}
}
const mapStateToProps = state => ({ selectedIndex : state.selectedIndex });
const TrackerIndex = withTracker(() => {
let ready = true
if(Meteor.isClient){
let categoriesSub = Meteor.subscribe('categories');
ready = categoriesSub.ready();
}
return {
categories: Categories.find({}).fetch(),
ready
}
})(Index)
export default connect(mapStateToProps)(TrackerIndex);
What shows on the screen is actually what I expect (list of categories) and it comes properly from Server as seen in the source code. I think the problem is that on server side I have my collection, then I get nothing on client side, then mysubscription and data again, but I do not get how to manage this difference due to the SSR, ideas how I could solve this ?

Related

Error Hydration failed with react-speech-recognition

I wanted to try speech recognition in NextJS 13. I installed react-speech-recognition and copy/pasted the provided example. But I am getting Error: Hydration failed because the initial UI does not match what was rendered on the server.
I tried to rollback react to v18.1, removed .next folder but it didn't help. I scrolled NextJS documentation about React Hydration Error, but I don't call windows and don't put div tag in p.
Any ideas what can be the issue?
Code:
'use client'
import 'regenerator-runtime/runtime'
import React from 'react'
import SpeechRecognition, {
useSpeechRecognition,
} from 'react-speech-recognition'
export default function page() {
const {
transcript,
listening,
resetTranscript,
browserSupportsSpeechRecognition,
} = useSpeechRecognition()
if (!browserSupportsSpeechRecognition) {
return <span>Browser doesn't support speech recognition.</span>
}
return (
<div>
<p>Microphone: {listening ? 'on' : 'off'}</p>
<button onClick={SpeechRecognition.startListening}>Start</button>
<button onClick={SpeechRecognition.stopListening}>Stop</button>
<button onClick={resetTranscript}>Reset</button>
<p>{transcript}</p>
</div>
)
}
The hydration error is caused by these lines:
if (!browserSupportsSpeechRecognition) {
return <span>Browser doesn't support speech recognition.</span>
}
Because you are using the 'use client' directive, this component behaves as traditional page components on previous Next.js versions (The page is pre-rendered and then sent to the client to be hydrated). The library you are using checks if webkitSpeechRecognition or SpeechRecognition exists in the window object in order to set the browserSupportsSpeechRecognition boolean, but window is not available server-side (it is undefined). The condition above evaluates to true thus creating the mismatch between what was rendered on the server and on the client-side's first render (You can view the page's source and you will notice that the not supported text was rendered on the server).
You can solve the issue using useState and useEffect hooks, taking advantage of the fact that useEffect only runs on the client-side:
'use client'
import React, { useState, useEffect } from 'react'
import 'regenerator-runtime/runtime'
import SpeechRecognition, {
useSpeechRecognition
} from 'react-speech-recognition'
const Page = () => {
const [speechRecognitionSupported, setSpeechRecognitionSupported] =
useState(null) // null or boolean
const {
transcript,
listening,
resetTranscript,
browserSupportsSpeechRecognition
} = useSpeechRecognition()
useEffect(() => {
// sets to true or false after component has been mounted
setSpeechRecognitionSupported(browserSupportsSpeechRecognition)
}, [browserSupportsSpeechRecognition])
if (speechRecognitionSupported === null) return null // return null on first render, can be a loading indicator
if (!speechRecognitionSupported) {
return <span>Browser does not support speech recognition.</span>
}
return (
<div>
<p>Microphone: {listening ? 'on' : 'off'}</p>
<button onClick={SpeechRecognition.startListening}>Start</button>
<button onClick={SpeechRecognition.stopListening}>Stop</button>
<button onClick={resetTranscript}>Reset</button>
<p>{transcript}</p>
</div>
)
}
export default Page
I had the same problem, but I think checking for server rendering (when the window object is undefined) is a little bit less messy solution:
const isServer = typeof window === "undefined";
if (!browserSupportsSpeechRecognition && !isServer) {
return <div>Your browser does not support speech recognition.</div>;
}
Works well!

URQL + React "typeError: Cannot read property '_react' of undefined"

I'm currently working through a urql+react tutorial found here while also adding graphQL API calls to my own application.
In the tutorial the following code is provided to send a query to the server and render components based on the result
const FEED_QUERY = gql`
{
feed {
links {
id
createdAt
url
description
}
}
}
`
const LinkList = () => {
const [result] = useQuery({ query: FEED_QUERY })
const { data, fetching, error } = result
if (fetching) return <div>Fetching</div>
if (error) return <div>Error</div>
const linksToRender = data.feed.links
return (
<div>
{linksToRender.map(link => <Link key={link.id} link={link} />)}
</div>
)
}
this code runs correctly when I implemented the tutorial myself but when I try to add this exact code to my own application I get the following error(s)
I have triple checked to make sure all the necessary urql and graphql dependencies are all installed in my application so I'm not sure what could be causing this issue.

React Apollo Query based on a condition

I'm currently building an app (hybrid mobile app) to show a list of records (places). Following is my requirement,
1) If the app is online, get the details from the server.
2) If the app is offline, get the details from the local storage.
I can get each condition working by its own. However (I'm fairly new to react and apollo-react), I'm not sure how to add a condition to the query.
below is an example of my query on getting the data from the server (I have this part working)
const client = new ApolloCient({
uri: "/graphql"
});
const PLACES_LIST = gql`
{
places {
id
title
}
}
`;
class PlacesList extends React.Component {
render() {
return (
<ApolloProvider client={client}>
<Query query={PLACES_LIST}>
{({ loading, data }) => {
if (loading) return "Loading....";
const { places } = data;
return places.map(place => (
<PlaceDetail
key={place.id}
place={place}
></PlaceDetail>
));
}}
</Query>
</ApolloProvider>
);
}
}
pseudocode for this I'm thinking would be,
if (online) {
# run apollo client
} else {
# read from the local storage
}
Can anyone point me in the correct direction. TIA.
Also, I'm using a latest version of react and I have the flexibility of using react hooks if that required.
const client = new ApolloCient({
uri: "/graphql"
});
const PLACES_LIST = gql`
{
places {
id
title
}
}
`;
class PlacesList extends React.Component {
render() {
return (
<ApolloProvider client={client}>
<Query query={PLACES_LIST}>
{({ loading, data, error }) => {
// There is also an error parameter from the hook
if (loading) return "Loading....";
// Here You can decide if its a connection error or smt other.
// I would recoment the fetchPolicy="network-only" prop for your <Query> in this case
if(error) {
return localstorage.getItem("Smt");
} else {
const { places } = data;
return places.map(place => (
<PlaceDetail
key={place.id}
place={place}
></PlaceDetail>
));
}
}}
</Query>
</ApolloProvider>
);
}
}
Maybe you can try to check for network connection using the navigator interface. I am not sure if navigator.onLine is available in hybrid mobile apps, but it would be easy to check.
You could do something like:
render(){
const isOnline = navigator.onLine
return (
<div>
{isOnline ? (
<ApolloProvider client={client}></ApolloProvider>
) : (
<LocalStorageComponent />
)}
</div>
);
}

React: setState after a graphQL request

I've been searching for a couple of hours now, but just can't seem to find the answer. See my code below. I'm requesting some metro-information to be used on an info-screen.
I'm getting the information, seeing as console.log works. However I'm having difficulty using this resulting oject. I want to use the data received, so that I can display when the next train arives. To this purpose I try to setState with the result, so that I can access the data-elements further down. However, now I'm stuck at setState giving me problems. I feel that I need to bind the function, but this.main = this.main.bind(this) doesn't work.
import React from "react";
import { GraphQLClient } from "graphql-request";
class Rutetider extends React.Component {
constructor(props) {
super(props);
this.state = {
stoppestedet: "rutetider lastes ned"
};
async function main() {
const endpoint = "https://api.entur.org/journeyplanner/2.0/index/graphql";
const graphQLClient = new GraphQLClient(endpoint, {
headers: {
ET: "lossfelt-tavle"
}
});
const query = `
{
stopPlace(id: "NSR:StopPlace:58249") {
id
name
estimatedCalls(timeRange: 3600, numberOfDepartures: 20) {
realtime
aimedArrivalTime
aimedDepartureTime
expectedArrivalTime
expectedDepartureTime
actualArrivalTime
actualDepartureTime
cancellation
notices {
text
}
situations {
summary {
value
}
}
date
forBoarding
forAlighting
destinationDisplay {
frontText
}
quay {
id
}
serviceJourney {
journeyPattern {
line {
id
name
transportMode
}
}
}
}
}
}
`;
const data = await graphQLClient.request(query);
console.log(data);
this.setState({ stoppestedet: data.stopPlace.name });
}
main().catch(error => console.error(error));
}
render() {
return (
<div>
rutetider
<div className="grid-container2">
<div>Mot byen</div>
<div>fra byen</div>
<div>{this.state.stoppestedet}</div>
</div>
</div>
);
}
}
export default Rutetider;
"probably easier" is to use integrated solution (apollo) than minimal, low level library. In most cases (as project grows), with more components fetching data managing separate GraphQLClient for all of them won't be an optimal solution. Apollo gives you centralised "fetching point", cache .. and many more.
Syntax error comes from function - in class it's enough to write async main()
https://codesandbox.io/s/l25r2kol7q
It probably would be better to save entire data in state and extract needed parts later (at render) and use this object as 'data-ready flag' (as I did for place - 'stoppestedet') - initally undefined (in constructor) for initial render (conditional rendering, some <Loading /> component):
render() {
if (!this.state.stoppestedet) return "rutetider lastes ned";
return (
<div>
rutetider
<div className="grid-container2">
<div>Mot byen</div>
<div>fra byen</div>
<div>{this.renderFetchedDataTable()}</div>
</div>

Determining when Relay is fetching query data

I'm using Reactjs and Relayjs in my web application. One of the pages, I call it memberList, is displaying a list of all users registered on the website.
This is a simplified version of my implementation:
render() {
{this.props.memberList.edges.length > 0 ?
<ol>
{this.props.memberList.edges.map(
(member, i) => {
return <li>{member.node.username}</li>;
}
)}
</ol>
: <span>No members to show!</span>}
}
And my RelayContainer:
export default Relay.createContainer(MemberList, {
fragments: {
classroom: () => Relay.QL`
fragment on Classroom {
id,
memberList(first: 100) {
edges {
node {
id,
username
}
}
},
}
`,
},
});
This works fine; it displays all the members of the classroom as expected. However, the page doesn't behave quite as I'd like it to:
When navigating to the page, or when refreshing, the <span> is rendered for a brief moment, because the this.props.memberList.edges array is empty.
Less than one second later, the props update and the array is no longer empty. This causes a re-render and the <ul> list with the members is now displayed instead - as expected.
I want to know when Relay is fetching data so I can determine if the memberList is actually empty or if its' properties cannot yet be determined because a query response is pending.
How can this be accomplished? I've searched for over 2 hours and I can only find relevant answers to mutations, which is not what I'm doing here. Thanks.
I'm surprised that your component is briefly rendering the span. By default the component shouldn't even be rendered if Relay hasn't finished fetching data.
Anyway, if you figure out what is going on there, Relay.Renderer has a render prop that you can use to achieve what you want. Here's an example (taken directly from the docs).
In this example, ErrorComponent and LoadingComponent simply display a static error message / loading indicator.
<Relay.Renderer
Container={ProfilePicture}
queryConfig={profileRoute}
environment={Relay.Store}
render={({done, error, props, retry, stale}) => {
if (error) {
return <ErrorComponent />;
} else if (props) {
return <ProfilePicture {...props} />;
} else {
return <LoadingComponent />;
}
}}
/>
If you're using Relay.RootContainer, it has some a similar renderLoading prop.

Resources