Does Next.js not need browser history when using Redux? - next.js

I'm trying to migrate my React app to Next.js. I keep having below error from export const history = createBrowserHistory(); in my configureStore.js.
Invariant Violation: Browser history needs a DOM
configureStore.js
import { createStore, combineReducers, compose, applyMiddleware } from "redux";
import { connectRouter, routerMiddleware } from "connected-react-router";
import { createBrowserHistory } from "history";
import thunk from "redux-thunk";
import user from "./modules/user";
import stores from "./modules/stores";
import info from "./modules/info";
export const history = createBrowserHistory();
const middlewares = [thunk, routerMiddleware(history)];
const reducer = combineReducers({
user,
stores,
info,
router: connectRouter(history)
});
export default function configureStore(preloadedState) {
return createStore(
reducer,
preloadedState,
compose(
applyMiddleware(
...middlewares
)
)
);
}
I've found that many things need to be changed when migrating React to Next.js as Next is a framework which requires its own code architecture as well as the difference between SSR and CSR. When I studied Next.js tutorials, there is a routing section which says that routing is differentiated based on CSR or SSR. Does that mean I cannot use browser history in Next.js? That's what I'm guessting. I'm still confused.

You are right. There is different between SSR and CSR. Next.js use Next Router for routing for CSR and if you need custom SSR, then you should ask for some help from frameworks like express.
By doing the CSR, nextjs will remember browser history and back button works as expected. However if you need to change the route to a very diffrernt route you can use any of these solutions:
import Router from 'next/router';
...
Router .push('/about');
Or
import Link from 'next/link';
...
<Link href="/about"><a>about</a></Link>
and if you need to do some extra work before routing then you should use:
Router.beforePopState(({ url, as, options }) => {...}
Migration would take some time and you need to remember next.js will take over the charge for routing and browser history automatically. Unless you customise it.

Related

Vue3 using script setup, how to access this.$router.push()

I am new to Vue3 and am struggling with some examples. I have a simple login component that should redirect to another page but isn't.
Here is my template:
And here is my script:
The problem is that the this.$router is not available, because this is undefined. All the examples that I can find use this coding pattern, but the this variable is undefined in the script setup coding pattern as far as I can tell. What is the alternative?
Thanks.
First off import the useRouter composable from vue-router, then use router to access the method.
import { useRouter } from 'vue-router';
const router = useRouter()
function login() {
router.push({ path: 'your-path-here' })
}
This is the equivalent of Options API's this.$router.push.

Next.js withPageAuthRequired with getStaticProps

According documentation #auth0/nextjs-auth0 we can use withPageAuthRequired for trigger login screen on pages required login.
short variant: export const getServerSideProps = withPageAuthRequired();
But what to do if I need to use getStaticProps for pre-render page at build time which can't be used together with getServerSideProps? Is there any way to use withPageAuthRequired on request static generated pages?
Right now I am using double check on client side for check auth. But I would rather use a server side check as i use on other pages.
P.S. There is way to use withPageAuthRequired on client side as well. This is not suitable for my use
Since getStaticProps() is used to build a static page (i.e., no server-side logic/rendering at request time), the auth check and redirect to login will have to happen on the client side.
You might be able to get the behaviour you want by sticking a proxy in front of the static resource (e.g., using Lambda#Edge), though I'm not very familiar with this approach yet.
From your question it sounds like you are already familiar with how to do the check/redirect on the client side, but for the benefit of others who come across this post in the future:
To fetch user information on the client side, add a <UserProvider> to your app, and call the useUser() hook in client-side components.
See docs:
Wrap your pages/_app.js component with the UserProvider component:
// pages/_app.js
import React from 'react';
import { UserProvider } from '#auth0/nextjs-auth0';
export default function App({ Component, pageProps }) {
return (
<UserProvider>
<Component {...pageProps} />
</UserProvider>
);
}
You can now determine if a user is authenticated by checking that the
user object returned by the useUser() hook is defined. You can
also log in or log out your users from the frontend layer of your
Next.js application by redirecting them to the appropriate
automatically-generated route:
// pages/index.js
import { useUser } from '#auth0/nextjs-auth0';
export default function Index() {
const { user, error, isLoading } = useUser();
if (isLoading) return <div>Loading...</div>;
if (error) return <div>{error.message}</div>;
if (user) {
return (
<div>
Welcome {user.name}!
Logout
</div>
);
}
return Login;
}
For other comprehensive examples, see the EXAMPLES.md
document.
An alternative approach that uses withPageAuthRequired() on the client side:
import React from 'react';
import { withPageAuthRequired } from '#auth0/nextjs-auth0';
import Layout from '../components/layout';
export default withPageAuthRequired(function Profile({ user }) {
return (
<Layout>
<h1>Profile</h1>
<h4>Profile</h4>
<pre data-testid="profile">{JSON.stringify(user, null, 2)}</pre>
</Layout>
);
});
Linked from additional examples.

Can we use redux states in a react component without using {connect}?

I usually import {connect} from react-redux to connect my React component to a Redux store.
Is there any other way to access the redux store from react components without using {connect}?
There is useSelector from React Redux Hooks
The selector is approximately equivalent to the mapStateToProps argument to connect conceptually. The selector will be called with the entire Redux store state as its only argument. The selector will be run whenever the function component renders (unless its reference hasn't changed since a previous render of the component so that a cached result can be returned by the hook without re-running the selector). useSelector() will also subscribe to the Redux store, and run your selector whenever an action is dispatched.
import React from 'react'
import { useSelector } from 'react-redux'
export const CounterComponent = () => {
const counter = useSelector(state => state.counter)
return <div>{counter}</div>
}

How do you snapshot test components with actions from Redux connect?

I want to snapshot test a react component that dispatches a redux action in its componentDidMount() handler. The action is given to the component through Redux's connect().
How should I mock the action?
Right now the action is imported in the component (see Newsfeed.js below). So how would I swap it out for a mock action in my test?
I'm using redux-thunk for my async actions, but that shouldn't matter much.
app/components/Newsfeed.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
// An action that makes an async request to get data
import { loadNewsfeedStories } from '../actions/Newsfeed';
class Newsfeed extends Component {
componentDidMount(){
this.props.loadNewsfeedStories();
}
...
}
export default connect(state => ({
stories: state.newsfeed.stories
}),
{
loadNewsfeedStories
})(Newsfeed)
app/tests/Newsfeed.test.js
import React from 'react';
import { Provider } from 'react-redux';
// This creates a mockStore using my reducers and a saved JSON state.
import { mockStore } from './MockState';
// The component to test
import Newsfeed from '../components/Newsfeed';
test('Newsfeed matches snapshot', () => {
const wrapper = mount(
<Provider store={mockStore}>
<Newsfeed />
</Provider>
);
expect(wrapper).toMatchSnapshot();
});
Possible Solutions
Export the unconnected component, and manually pass in all props & mock actions. Will be a lot of extra coding compared to just using the mockStore & provider. Also we won't be testing the 'connected' component.
Use something like Nock? Seems to intercept HTTP calls, so the ajax requests wouldn't actually go anywhere.
For axios, there is a lib called moxios - https://github.com/mzabriskie/moxios
You could try exporting a different connected component, but with a mocked "mapDispatchToProps" function. This way, no action will get through.
But personally, when testing components. I write a mock reducer, these should not change the state but record all dispatched actions (Jests mock functions are very useful for this). This way, you can also test if the correct action is dispatched when clicking a button,...
It's a bad idea to test the component, redux store, and mock http requests at the same time because unit tests should be small.
Another option would be to avoid executing business logic in componentDidMount.
Instead of writing
componentDidMount(){
this.props.loadNewsfeedStories();
}
You can move this logic to the redux module (depends on libs you use).
If you use only redux-thunk without libraries like saga or redux-logic, you can use this https://github.com/Rezonans/redux-async-connect.
but record all dispatched actions
Redux thunks are hard to test. Your mock dispatcher will receive only an array of functions. And you can't verify if a function was created by loadNewsfeedStories() or by someAnotherThunk().

Angular 2 dynamic routing

I'm converting an existing db (Wordpress) driven site to an Angular 2 front end, keeping the backend as is. I'm at the routing stage and I want to create the routes from JSON data pulled from the db.
So, I've created a service to pull the data from the db and a separate routing module to register the routes:
app-routing.module.ts
import { NgModule } from '#angular/core';
import { RouterModule, Routes } from '#angular/router';
import { NavService } from './nav.service';
var navService: NavService;
const routes: Routes = navService.getRoutes();
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRouting{}
nav.service.ts
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/toPromise';
#Injectable()
export class SiteNavService{
private _navUrl = 'pathtofile/?action=ajax_site_nav';
constructor(private _http: Http){}
getNav(){
return this._http.get(this._navUrl)
.map( res => res.json())
.toPromise();
}
}
The service is registered in the app.module.ts providers array and the routing module in the imports array. I can't get nav.service to work in the routing module file.
I've just now been working on trying to add routes dynamically to angular 2 and found that getting the current routes with
this.router.config
and then changing them and applying the changes with
this.router.resetConfig(newRoutes);
Worked pretty well
I have my demo project here that might help where team members are dynamically added from json https://github.com/davidejones/angular2-routertest
This is now possible using Xo for Angular: https://wordpress.org/plugins/xo-for-angular/
Xo creates dynamic routes based on your WordPress content, posts, pages, etc.
The secret sauce making this possible is the use of loadChildren in the Angular Route definition to lazy load a particular module. This is necessary as otherwise Route expects a component from an import that isn't realistic.
Example of this output from the docs site API: https://angularxo.io/xo-api/routes/get
Then in the Angular App using the angular-xo module these routes are loaded and updated by calling: router.resetConfig(response.routes);
Taken from this file: https://github.com/WarriorRocker/angular-xo-node/blob/master/lib/src/services/route.service.ts
Full disclosure, I am the author of the above plugin.
More docs (work in progress): https://angularxo.io
Example theme: https://github.com/WarriorRocker/angular-xo-material

Resources