How can I mix between a ssr component and a client component in Nextjs 13? - next.js

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>
);
} );
}

Related

How to Call API In NextJS Using Redux With SSR?

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;

Dynamic data fetching in dynamic pages (Next.js and Redux Toolkit)

In this dynamic page, we have to fetch different APIs by coming from different routes. I want to dynamically fetch and list not only fetchProducts, but also crops from any data on this page. For example: How should we do this? the code you see is from index.js
File structure:
product/
[category]
[slug].js
index.js
import React, { useEffect } from "react";
import { ProductCard } from "#components/Product/ProductCategory/ProductList";
import { useDispatch, useSelector } from "react-redux";
import { fetchProducts } from "redux/slices/product/productCategorySlice";
import { useRouter } from "next/router";
import Link from "next/link";
const CategoryDetail = () => {
const router = useRouter();
const { category } = router.query;
const productList = useSelector((state) => state.productCategory);
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchProducts(category));
}, [dispatch, category]);
return (
<div className="container max-w-full">
<div className="flex flex-row py-24">
<div className="w-80per pl-30">
{productList.loading && console.log("Loading now...")}
{!productList.loading && productList.error
? console.log(productList.error)
: null}
{!productList.loading && productList.productList.data
? (
<div className="grid grid-cols-4 gap-5">
{productList.productList.data.data.map((item) => (
<ProductCard key={item} data={item} modal={false} />
))}
</div>
)
: null}
</div>
</div>
</div>
);
};
export default CategoryDetail;

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

How to navigate to a url with different 'id' property using axios library in redux

I am working on a redux project where I have a list of posts(like Blog Post) and each post has an unique id.Also,I have a different component which displays the detail of a particular post(I have named it Post_Detail component). So what I want to do is when I click on a particular post from the list of posts,I want to navigate to the page which displays the detail of that particular post.
My action creator for displaying a selected post is:
//Action Creator for displaying a selected post
export function fetchPost(id) {
const request = axios.get(`${API}/posts/${id}`,{headers});
return dispatch => {
return request.then(({data}) => {
console.log(data);
dispatch({
type: FETCH_POST,
payload: data
})
})
}
}
My Post_Detail component for displaying the selected post is:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchPost, deletePost } from '../actions/posts_action';
class PostDetail extends Component {
componentDidMount() {
const { id } = this.props.match.params;
this.props.fetchPost(id);
console.log(id)
}
//Function for deleting on click using action creator
onDeleteClick() {
const { id } = this.props.match.params;
this.props.deletePost(id, () => {
this.props.history.push('/');
});
}
render() {
const { post } = this.props;
if (!post) {
return <div>Loading...</div>
}
return(
<div>
<div>
<h3>{post.title}</h3>
<h6>Categories: {post.category}</h6>
<p>{post.body}</p>
</div>
<button
className="btn btn-danger pull-xs-right"
onClick={this.onDeleteClick.bind(this)} >
Delete Post
</button>
</div>
);
}
}
function mapStateToProps({ posts }, ownProps) {
return { post: posts[ownProps.match.params.id] };
}
export default connect(mapStateToProps, { fetchPost, deletePost })(PostDetail);
My homepage for listing all the available posts is:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import { BrowserRouter, Route } from 'react-router-dom';
import thunk from 'redux-thunk';
import './index.css';
import App from './App';
import reducers from './reducers/index.js'
import Posts from './components/posts_index';
import CreatePost from './components/new_post';
import PostDetail from './components/post_detail';
import CategoryView from './components/category';
import { compose } from 'redux';
import { Link } from 'react-router-dom';
//const createStoreWithMiddleware = createStore(reducers,applyMiddleware(thunk));
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const createStoreWithMiddleware = createStore(reducers, composeEnhancers(applyMiddleware(thunk)));
ReactDOM.render(
<Provider store={createStoreWithMiddleware}>
<BrowserRouter>
<div>
<Route path="/new" component={CreatePost} />
<Route path="/posts/:id" component={PostDetail} />
<Route exact path="/" component={Posts} />
<Route path="/:category/posts" component={CategoryView} />
</div>
</BrowserRouter>
</Provider> , document.getElementById('root'));
So, what is happening now is if I try to navigate to the post using the "id" for that post in the url like : http://localhost:3000/posts/8xf0y6ziyjabvozdd253nd where "8xf0y6ziyjabvozdd253nd" is the id of that post,I can navigate to the Post_Detail component and see the details of that post.But,I am not able to figure out how to navigate to the Post_Detail component when the post is clicked from the list of posts in the homepage. I know that I should use a "Link" from 'react-router-dom' and navigate to the page passing in the url in "to" property of the link, but I don't know how to dynamically change the id in the link. Can anyone please guide me how to proceed?

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