How to Call API In NextJS Using Redux With SSR? - redux

Need One Help i am trying to call api in nextjs using
redux(Here,SSR is not possible)
and getServerSideProps(Here, SSR is Possible but without redux) in both case API Called Successfully. If i used it individually bot are working well but now i want to merge it both and i have read about next-redux-wrapper but when i am integrate it.
API Called Using GetServerSideProps()
index.js
`
import Product from "./product-cart";
import React from "react";
import styles from "../styles/Product.module.css";
import axios from "axios";
const Home = ({ products, loading }) => {
return (
<>
{loading ? (
<h1>Loading...</h1>
) : (
<>
<div className={styles.banner}>
<p>Welcome to ecommerce!</p>
<h1>FIND AMAZING PRODUCT BELOW</h1>
<a href="#container">
<button>Scroll</button>
</a>
</div>
<h2 className={styles.homeHeading}>Featured Product</h2>
<div className={styles.container} id="container">
{products &&
products.map((products) => (
<Product key={products._id} products={products} />
))}
</div>
</>
)}
</>
);
};
export default Home;
export async function getServerSideProps(context) {
let link = `http://localhost:5000/api/v1/product`;
const { data } = await axios.get(link);
return { props: { products: data.product } };
}
`
API Called Using Redux
_app.js
`
import { Provider } from "react-redux";
import store, { wrapper } from "../redux/store";
import "../styles/globals.css";
function MyApp({ Component, pageProps }) {
return (
<>
<Provider store={store}>
<Component {...pageProps} />
</Provider>
</>
);
}
// export default wrapper.withRedux(MyApp);
export default MyApp;
`
index.js(GetProduct() Created In Action File)
`
import Product from "./product-cart";
import React from "react";
import styles from "../styles/Product.module.css";
import { clearErrors, getProduct } from "../redux/Actions/ProductAction";
import { useSelector, useDispatch, connect } from "react-redux";
import axios from "axios";
import { wrapper } from "../redux/store";
const Home = ({ products, loading }) => {
const { loading, error, products } = useSelector(
(state) => state.products || {}
);
const dispatch = useDispatch();
React.useEffect(() => {
if (error) {
dispatch(clearErrors());
}
dispatch(getProduct());
}, [dispatch]);
return (
<>
{loading ? (
<h1>Loading...</h1>
) : (
<>
<div className={styles.banner}>
<p>Welcome to ecommerce!</p>
<h1>FIND AMAZING PRODUCT BELOW</h1>
<a href="#container">
<button>Scroll</button>
</a>
</div>
<div suppressHydrationWarning>
{process.browser ? "browser" : "server"}
</div>
<h2 className={styles.homeHeading}>Featured Product</h2>
<div className={styles.container} id="container">
{products &&
products.map((products) => (
<Product key={products._id} products={products} />
))}
</div>
</>
)}
</>
);
};
export default Home;
`
If you need more information let me know
TypeError: makeStore is not a function
enter image description here
Try to Use Next-redux-wrapper but not able to find solution regarding that
_app.js
import { Provider } from "react-redux";
import store, { wrapper } from "../redux/store";
import "../styles/globals.css";
function MyApp({ Component, pageProps }) {
return (
<>
<Provider store={store}>
<Component {...pageProps} />
</Provider>
</>
);
}
export default wrapper.withRedux(MyApp);
store.js
import {
createStore,
combineReducers,
applyMiddleware,
legacy_createStore,
} from "redux";
import thunk from "redux-thunk";
// import { createStore } from "redux";
import { createWrapper, HYDRATE } from "next-redux-wrapper";
import { composeWithDevTools } from "redux-devtools-extension";
import {
adminProductEDReducer,
AdminProductReducer,
newProductReducer,
newReviewReducer,
productDetailReducer,
productReducer,
} from "./Reducers/ProductReducer";
import {
userReducer,
profileReducer,
forgotPasswordReducer,
allUserReducer,
userDetailsReducer,
} from "./Reducers/UserReducers";
const reducer = combineReducers({
products: productReducer,
productDetails: productDetailReducer,
adminProduct: AdminProductReducer,
newProduct: newProductReducer,
user: userReducer,
profile: profileReducer,
forgotPassword: forgotPasswordReducer,
newReview: newReviewReducer,
EditDelProduct: adminProductEDReducer,
allUser: allUserReducer,
userDetail: userDetailsReducer,
});
// if the value is in cart otherwise it will be blank and we can store cartitems in localstorage
let initialState = {};
const middleware = [thunk];
const store = legacy_createStore(
reducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
// export an assembled wrapper
export const wrapper = createWrapper(store, { debug: true });
export default store;

Related

How can I mix between a ssr component and a client component in Nextjs 13?

The point is to implement a dynamic SSR component can be re-rendered by a search input.
I solved this by creating a layout.tsx file on my specific router then import children which made me dynamic render ssr component by the client component:
Layout.tsx
import React, { Suspense } from "react";
import Search from "./Search";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="layout">
<Search />
{children}
</div>
);
}
Search.tsx
"use client";
import { FormEvent, useState } from "react";
import { useRouter } from "next/navigation";
export default function Search() {
const [text, setText] = useState<string>("")
const router: any = useRouter();
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
setText('')
router.push(`/definition/${text}`)
}
return (
<form onSubmit={handleSubmit} className='search'>
<input onChange={(e) => setText(e.target.value)}
value={text} type="text"
placeholder={"write to search"} />
</form>
);
} );
}

Hydration failed because the initial UI does not match what was rendered on the server (Next.js v12.3.1)

import { Container } from 'react-bootstrap'
import { useDispatch, useSelector } from 'react-redux'
import Layout from '../../components/Layout/Layout'
const CartScreen = () => {
const cart = useSelector(state => state.cart)
const { cartItems } = cart
console.log(cartItems)
return (
<Layout>
<Container>
<div>
<h1>Shopping Cart</h1>
{cartItems && cartItems.length === 0 && <p>Your cart is empty </p>}
</div>
</Container>
</Layout>
)
}
export default CartScreen
This code gives me that Hydration Error only when 'cart' is empty
I don't know how to use useEffect on this and I use redux toolkit.

Avoid importing Apollo on specific paths in Nextjs

In Nextjs, I would like to avoid importing Apollo lib and ApolloProvider on specific paths such as the homepage since the bundle is huge.
Here is how I tried to proceed without success (it's always bundled):
import { ApolloProvider } from "#apollo/client";
import type { NextPageWithLayout } from "next";
import type { AppProps } from "next/app";
import { useApollo } from "#/libraries/apollo/client";
type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout;
err: any;
};
export default function MyApp({ Component, pageProps, err, ...appProps }: AppPropsWithLayout) {
const apolloClient = useApollo(pageProps.initialApolloState);
if (["/"].includes(appProps.router.pathname))
return (
<Component {...pageProps} err={err} />
);
const getLayout = Component.getLayout || ((page) => page);
return (
<ApolloProvider client={apolloClient}>
{getLayout(<Component {...pageProps} err={err} />)}
</ApolloProvider>
);
}
Do you think we can use the ApolloProvider in the getLayout functions of all the other pages? Will it persist between page changes?

How to setup react-redux-firestore in NEXT.JS

I am migrating from my Create React App (client-side-rendering) to Next JS (server-side rendering) due to SEO reasons. Migrating was going well until using React-Redux-Firebase / Firestore. This is the page I am trying to load:
Discover.js
import React, { Component } from 'react'
import { firestoreConnect, isEmpty } from 'react-redux-firebase';
import { compose } from 'redux'
import { connect } from "react-redux";
import { blogpostsQuery } from '../blogposts/blogpostsQuery';
import DiscoverList from "./DiscoverList";
const mapState = (state, ownProps) => {
let blogposts = {};
blogposts =
!isEmpty(state.firestore.ordered.blogposts) &&
state.firestore.ordered.blogposts;
return {
blogposts,
};
};
class DiscoverPage extends Component {
render() {
const {blogposts} = this.props
return (
<div className="container">
<div className="hero">
<h1>Discover stories</h1>
<p className="lead">Blogpost published:</p>
</div>
<DiscoverList blogposts={blogposts} />
</div>
)
}
}
export default compose(
firestoreConnect(() => blogpostsQuery()),
connect(mapState, null)
)(DiscoverPage)
The error I received is this:
ReferenceError: XMLHttpRequest is not defined
at Rn.ca (/Users/fridovandriem/timepath/node_modules/firebase/firebase-firestore.js:1:36966)
at Ie (/Users/fridovandriem/timepath/node_modules/firebase/firebase-firestore.js:1:18723)
at Se (/Users/fridovandriem/timepath/node_modules/firebase/firebase-firestore.js:1:18385)
at Kn.a.Ia (/Users/fridovandriem/timepath/node_modules/firebase/firebase-firestore.js:1:39600)
at jt (/Users/fridovandriem/timepath/node_modules/firebase/firebase-firestore.js:1:15360)
error Command failed with exit code 1.
I wasn't the only one with this problem and I have found the solution on GitHub by prescottprue:
https://github.com/prescottprue/react-redux-firebase/issues/72
Including documentation: http://react-redux-firebase.com/docs/recipes/ssr.html
// needed to fix "Error: The XMLHttpRequest compatibility library was not found."
global.XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest
The problem is (sorry i am new developer) I have no idea where to add this line of code? Adding it to _app.js doesnt work.. i've added https://www.npmjs.com/package/xmlhttprequest but still no luck...
-app.js
import App from "next/app";
import { Provider } from "react-redux";
import React, { Fragment } from "react";
import withRedux from "next-redux-wrapper";
import "../src/styles.css";
import configureStore from "../src/app/store/configureStore";
import Header from "../src/app/layout/Header";
import NavBar from "../src/app/layout/nav/Navbar/NavBar";
import Footer from "../src/app/layout/Footer";
global.XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest
class MyApp extends App {
static async getInitialProps({ Component, ctx }) {
const pageProps = Component.getInitialProps
? await Component.getInitialProps(ctx)
: {};
//Anything returned here can be accessed by the client
return { pageProps: pageProps };
}
render() {
const { Component, pageProps, store } = this.props;
return (
<Fragment>
<Header />
<NavBar />
<Provider store={store}>
<Component {...pageProps} />
</Provider>
<Footer />
</Fragment>
);
}
}
export default withRedux(configureStore)(MyApp);
Could somebody help me?
Many thanks
Frido

Upgrading from createContainer to withTracker with React Router V4

I upgraded meteor and I started receiving deprecation warning for createContainer(). As a result, I've tried to implement withTracker however now I'm getting Component(...): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.. I'm not sure what I'm missing here, can someone point out my error.
Path: App.jsx
import { Meteor } from 'meteor/meteor';
import React from 'react';
import PropTypes from 'prop-types';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { withTracker } from 'meteor/react-meteor-data';
// IsCandidate Spefic Routes
import TestContainer from '../../containers/candidate/TestContainer';
const App = appProps => (
<Router>
<ScrollToTop>
<div className="bgColor">
<NavBar {...appProps} />
<Grid className="main-page-container">
<Switch>
{/* candidate routes */}
<IsCandidate exact path="/candidate/testpage/:id" component={withTracker(TestContainer)} {...appProps} />
{/* IsPublic routes */}
<Route render={function () {
return <p>Page not found</p>;
}}
/>
</Switch>
</Grid>
</div>
</ScrollToTop>
</Router>
);
App.propTypes = {
loggingIn: PropTypes.bool,
isCandidate: PropTypes.bool
};
export default createContainer(() => {
const loggingIn = Meteor.loggingIn();
return {
loggingIn,
isCandidate: !loggingIn && !!Meteor.userId() && !!Roles.userIsInRole(Meteor.userId(), 'isCandidate'),
};
}, App);
Path: IsCandidate.jsx
import React from 'react';
import PropTypes from 'prop-types'; // ES6
import { Route, Redirect } from 'react-router-dom';
const IsCandidate = ({ loggingIn, isCandidate, component: Component, ...rest }) => (
<Route
{...rest}
render={(props) => {
if (loggingIn) return <div />;
return isCandidate ?
(<Component loggingIn={loggingIn} isCandidate={isCandidate} {...rest} {...props} />) :
(<Redirect to="/login" />);
}}
/>
);
IsCandidate.propTypes = {
loggingIn: PropTypes.bool,
isCandidate: PropTypes.bool,
component: PropTypes.func
};
export default IsCandidate;
Path: Testcontainer.jsx
import { Meteor } from 'meteor/meteor';
import { withTracker } from 'meteor/react-meteor-data';
import { Test } from '../../../api/test/test';
import TestPage from '../../pages/candidate/TestPage';
export default TestContainer = withTracker(({ match }) => {
const testHandle = Meteor.subscribe('test', match.params.id);
const loadingTest = !testHandle.ready();
const testCollection = Test.findOne(match.params.id);
const testExist = !loadingTest && !!testCollection;
return {
loadingTest,
testExist,
testCollection: testExist ? testCollection : {}
};
}, TestPage);
Update
export default withTracker(() => {
const loggingIn = Meteor.loggingIn();
return {
loggingIn,
isCandidate: !loggingIn && !!Meteor.userId() && !!Roles.userIsInRole(Meteor.userId(), 'isCandidate'),
isEmployer: !loggingIn && !!Meteor.userId() && !!Roles.userIsInRole(Meteor.userId(), 'isEmployer'),
isAdmin: !loggingIn && !!Meteor.userId() && !!Roles.userIsInRole(Meteor.userId(), 'isAdmin')
};
})(App);
In App.jsx you import withTracker but use createContainer.
withTracker takes only one argument (your reactive function) and wraps your child component where createContainer took 2 arguments (function and component).
createContainer(fn, C);
withTracker(fn)(C);
EDIT
Remove the withTracker call in App.js from this line:
<IsCandidate exact path="/candidate/testpage/:id" component={withTracker(TestContainer)} {...appProps} />
so it becomes
<IsCandidate exact path="/candidate/testpage/:id" component={TestContainer} {...appProps} />
How about it?

Resources