how to detect is mobile SSR - reactjs - javascript

I'm using mobile-detect library and this tutorial:
link
/src/utiles/isMobile.tsx:
import MobileDetect from "mobile-detect";
import { GetServerSidePropsContext } from "next";
export const getIsSsrMobile = (context: GetServerSidePropsContext) => {
const md = new MobileDetect(context.req.headers["user-agent"] as string);
return Boolean(md.mobile());
};
/src/hooks/useIsMobile.js:
import { useContext } from "react";
import { IsSsrMobileContext } from "../contexts/isSsrMobileContext";
export const useIsMobile = () => {
const isSsrMobile = useContext(IsSsrMobileContext);
return isSsrMobile;
};
/src/contexts/isSsrMobileContext.js:
import { createContext } from "react";
export const IsSsrMobileContext = createContext(false);
my `_app.tsx`:
import { IsSsrMobileContext } from "./../src/contexts/isSsrMobileContext";
function MyApp({ Component, pageProps }: AppProps <{ isSsrMobile: boolean }>): JSX.Element {
return (
<IsSsrMobileContext.Provider value={pageProps.isSsrMobile}>
<Provider store={store}>
<Component {...pageProps} />
</Provider>
</IsSsrMobileContext.Provider>
);
}
export default MyApp;
inside my pages/index.tsx:
console.log("useIsMobile",useIsMobile())
It shows null.

Related

createContext undefined

When I try to use createContext() the console gives me this error:
App.js:6
Uncaught TypeError: Cannot destructure property 'consoleLogFunction' of '(0 , react__WEBPACK_IMPORTED_MODULE_1__.useContext)(...)' as it is null.
I've seen others asking questions about this here in Stack Overflow but I can't find a solution.
GlobalContext.js
import React from 'react'
import { createContext } from 'react'
export const AppContext = createContext();
function GlobalContext() {
const consoleLogFunction = () => {
console.log("ok")
}
return (
<AppContext.Provider value={{consoleLogFunction}}></AppContext.Provider>
)
}
export default GlobalContext
App.js
import "./index.css";
import { useContext, useEffect } from "react";
import { AppContext } from "./components/GlobalContext";
function App() {
const { consoleLogFunction } = useContext(AppContext);
useEffect(() => {
consoleLogFunction();
}, []);
return (
<AppContext>
<div>home</div>
</AppContext>
);
}
export default App;
You don't need to export 'AppContext', creating the provider and exporting that is good enough.
Try this, I've also made a couple of modifications to make it easier to use the context later:
GlobalContext.js
import React from 'react'
import { createContext, useContext } from 'react'
const AppContext = createContext();
function GlobalContext({ children }) {
const consoleLogFunction = () => {
console.log("ok")
}
return (
<AppContext.Provider value={{consoleLogFunction}}>
{ children }
</AppContext.Provider>
)
}
export default GlobalContext;
// This is a helper
export const useGlobalContext = () => useContext(AppContext);
Home.js
import { useEffect } from "react";
import { useGlobalContext } from "./components/GlobalContext";
export const Home = ({ children }) => {
const { consoleLogFunction } = useGlobalContext();
useEffect(() => {
consoleLogFunction();
}, []);
return(
<div>home</div>
)
};
App.js
import "./index.css";
import { useEffect } from "react";
import GlobalContext from "./components/GlobalContext"
import { Home } from "./components/Home";
function App() {
return(
<GlobalContext>
<Home />
</GlobalContext>
);
}
export default App;
hello man the problem is because App component is not wrapped inside GlobalContext . and in the GlobalContext component you should handle the children prop.
it will work when doing it like this example :
import { useEffect, useContext, createContext } from "react";
import "./styles.css";
export const AppContext = createContext();
function GlobalContext(props) {
const consoleLogFunction = () => {
console.log("ok");
};
return (
<AppContext.Provider value={{ consoleLogFunction }}>
{props.children}
</AppContext.Provider>
);
}
const Home = () => {
const { consoleLogFunction } = useContext(AppContext);
useEffect(() => {
consoleLogFunction();
}, []);
return <div>home</div>;
};
export default function App() {
return (
<GlobalContext>
<Home />
</GlobalContext>
);
}
Hope this help.

Nextjs TypeScript useContext through the pages

Edit:
I was not using
import Link from 'next/link';
in my Header.tsx component.
Now it works.
Don't know what I am doing wrong right here.
I try to make global state (to indicate if user is logged in or not) that just flows through the pages and I try to do it with react's hook useContext.
It is not working like how I would like to make it work. When I toggleLogged and go to another page, the context has default value and not the changed one.
I think the problem I am facing is something really small or a fundemantal thing that I just can't see.
Here is how my UserContext.ts file looks:
import { createContext, useContext } from 'react';
type userContextType = {
isLogged: boolean;
toggleLogged: () => void;
};
const userContextDefault: userContextType = {
isLogged: false,
toggleLogged: () => {},
};
export const UserContext = createContext(userContextDefault);
export function useUserContext() {
return useContext(UserContext);
}
Here is my Layout.tsx:
import React, { useState } from 'react';
import Header from './Header';
const Layout = (props: any) => {
const { children } = props;
return (
<div className='container'>
<Header />
{children}
</div>
);
};
export default Layout;
And lastly here is my _app.tsx:
import type { AppContext, AppProps } from 'next/app';
import Layout from '../components/Layout';
import { useState } from 'react';
import { UserContext } from '../components/UserContext';
import '../styles/globals.css';
const MyApp = ({ Component, pageProps }: AppProps) => {
const [isLogged, setIsLogged] = useState(true);
const toggleLogged = () => {
setIsLogged((isLogged) => !isLogged);
};
return (
<UserContext.Provider value={{ isLogged, toggleLogged }}>
<Layout>
<Component {...pageProps} />;
</Layout>
</UserContext.Provider>
);
};
export default MyApp;
Thanks for any help in advance.
You need to return the provider in the context, and then reference the instance in _app.js.
Here's my AuthContext (as an example). Don't worry about the specific code, but use my implementation as the foundation, and you'll be up and running in no time!
import { createContext, useContext, useEffect, useState } from 'react'
import { auth } from '../firebase'
const AuthContext = createContext()
export function useAuth() {
return useContext(AuthContext)
}
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState()
const [loading, setLoading] = useState(true)
function login(email, password) {
return auth.signInWithEmailAndPassword(email, password)
}
function signOut() {
return auth.signOut();
}
function signUp(email, password) {
return auth.createUserWithEmailAndPassword(email, password)
}
function getUser() {
return auth.currentUser
}
function isAdmin() {
return auth.currentUser.getIdTokenResult()
.then((idTokenResult) => {
if (!!idTokenResult.claims.admin) {
return true
} else {
return false
}
})
}
function isEditor() {
}
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(user => {
setCurrentUser(user)
setLoading(false)
})
return unsubscribe
}, [])
const value = {
currentUser,
getUser,
login,
signOut,
signUp
}
return (
<AuthContext.Provider value={value}>
{ !loading && children }
</AuthContext.Provider>
)
}
My _app.js file:
import '../styles/globals.scss'
import { motion, AnimatePresence } from 'framer-motion'
import { useRouter } from 'next/router'
import Header from '../components/Header'
import Footer from '../components/Footer'
import { AuthProvider } from '../contexts/AuthContext'
import { CartProvider } from '../contexts/CartContext'
import { ThemeProvider } from '#material-ui/core'
import theme from '../styles/theme'
export default function App({ Component, pageProps }) {
const router = useRouter()
return(
<AnimatePresence exitBeforeEnter>
<CartProvider>
<AuthProvider>
<ThemeProvider theme={theme}>
<Header />
<motion.div key={router.pathname} className="main">
<Component { ...pageProps } />
<Footer />
</motion.div>
</ThemeProvider>
</AuthProvider>
</CartProvider>
</AnimatePresence>
)
}

Context API dispatch (consumer) in _app.js class component Next.js

I need to use dispatch Context API methods in _app.js.
The main limitation is that I use React hooks along with Context API, since _app.js is a Class, I can't use hooks within it.
My code:
// store.js
import React, { createContext, useContext, useReducer } from "react";
import mainReducer from "../store/reducers";
const AppStateContext = createContext();
const AppDispatchContext = createContext();
const initialState = {
filters: {
diet: {
selected: []
}
}
};
const useAppState = () => useContext(AppStateContext);
const useAppDispatch = () => useContext(AppDispatchContext);
const useApp = () => [useAppState(), useAppDispatch()];
const AppProvider = ({ children }) => {
const [state, dispatch] = useReducer(mainReducer, initialState);
return (
<AppStateContext.Provider value={state}>
<AppDispatchContext.Provider value={dispatch}>
{children}
</AppDispatchContext.Provider>
</AppStateContext.Provider>
);
};
export { AppProvider, useAppState, useAppDispatch, useApp };
// _app.js
import App from "next/app";
import React from "react";
import { AppProvider } from "../store";
class MyApp extends App {
componentDidMount() {
/***********************************/
// HERE I WOULD LIKE TO USE DISPATCH
/***********************************/
}
render() {
const { Component, router, pageProps } = this.props;
return (
<AppProvider>
<Component {...pageProps} />
</AppProvider>
);
}
}
export default MyApp;
If you really want to use hooks, then just put a wrapper around _app.js like this:
import React from 'react'
import App from 'next/app'
function MyComponent({ children }) {
// You can use hooks here
return <>{children}</>
}
class MyApp extends App {
render() {
const { Component, pageProps } = this.props
return (
<MyComponent>
<Component {...pageProps} />
</MyComponent>
)
}
}
export default MyApp

How to config appWithTranslation(nexti18next + withRedux (next-redux-wrapper) in __app.js

I'm tranfer reactjs project to nextjs with react-redux, redux, nexti18next, next-redux-wrapper here is _app.js and redux.js it run right but if i add withRedux it is error. Please tell me how to resolve this problem or advide me some solution!
import React from "react";
import App, { Container } from "next/app";
import { appWithTranslation } from "../i18n";
import ScrollToTop from "../components/ScrollToTop";
import { createBrowserHistory } from "history";
import { Provider } from "react-redux";
import storeConfig from "../redux";
import compose from "recompose/compose";
const history = createBrowserHistory();
const store = storeConfig();
import withRedux from "next-redux-wrapper";
class MyApp extends App {
static async getInitialProps({ Component, ctx }) {
let pageProps = {};
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx);
}
return { pageProps };
}
render() {
const { Component, pageProps } = this.props;
console.log(store)
return (
<Container>
<ScrollToTop>
<Provider store={store}>
<Component {...pageProps} />
</Provider>
</ScrollToTop>
</Container>
);
}
}
export default compose(appWithTranslation)(MyApp);
import { createStore, combineReducers, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import getQuote, { initGetQuoteState } from './Modules/GetQuote';
import getSlider, { initGetSliderState } from './Modules/GetSlider';
import getUser, {initGetUserState } from './Modules/User';
import {composeWithDevTools } from 'redux-devtools-extension'
const initState = {
getQuote: initGetQuoteState,
getSlider: initGetSliderState,
getUser: initGetUserState
};
const rooteReducer = combineReducers({
getQuote,
getSlider,
getUser
});
const store = () => createStore(rooteReducer, initState, composeWithDevTools(applyMiddleware(thunk)))
export default store;
Here is example of NextJS/Typescript/Redux/Styled-Components/next-i18next app sample.
// _app.tsx
import * as React from "react";
import App, { AppInitialProps, AppContext } from "next/app";
import withRedux from "next-redux-wrapper";
import { Provider } from "react-redux";
import { ThemeProvider } from "styled-components";
import { theme } from "#Definitions/Styled";
import { appWithTranslation } from "#Server/i18n";
import { AppWithStore } from "#Interfaces";
import { makeStore } from "#Redux";
import "#Static/css/main.scss";
class ProgressiveWebApp extends App<AppWithStore> {
static async getInitialProps({
Component,
ctx,
}: AppContext): Promise<AppInitialProps> {
const pageProps = Component.getInitialProps
? await Component.getInitialProps(ctx)
: {};
return { pageProps };
}
render() {
const { Component, pageProps, store } = this.props;
return (
<Provider store={store}>
<ThemeProvider theme={theme}>
<Component {...pageProps} />
</ThemeProvider>
</Provider>
);
}
}
export default withRedux(makeStore)(appWithTranslation(ProgressiveWebApp));
//i18.ts
import NextI18Next from "next-i18next";
const NextI18NextInstance = new NextI18Next({
defaultLanguage: "en",
otherLanguages: ["es"],
});
export const {
appWithTranslation,
withTranslation,
useTranslation,
i18n,
} = NextI18NextInstance;
export default NextI18NextInstance;
and I have path alias in tsconfig
"paths": {
"#Definitions/*": ["definitions/*"],
"#Interfaces": ["interfaces"],
"#Redux": ["redux-store"],
"#Server/*": ["server/*"]
}
hope this helps somebody!

Cannot read property 'type' of undefined - redux

I'm trying to dispatch an action, but it returns "type" of undefined. I suspect Redux Thunk is not working properly.
Before I was dispatching the same action from the parent component and it was working.
Entry point
import React, { Component } from 'react'
import { Provider } from 'react-redux'
import configureStore from '../ConfigureStore'
import '../App.css';
import App from './theapp/theAppContainer';
const store = configureStore()
class Root extends Component {
render() {
return (
<Provider store={store}>
<App />
</Provider>
)
}
}
export default Root;
Store
import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
import { createLogger } from 'redux-logger'
import allReducers from './reducers/index'
const loggerMiddleware = createLogger()
export default function configureStore() {
return createStore(
allReducers,
applyMiddleware(thunkMiddleware, loggerMiddleware)
)
}
The app - routing. Before I was dispatching the action at this level and it was working.
import React, { Component } from 'react'
import Cards from '../templates/cards/CardsContainer'
import EditApp from '../pages/editApp/EditApp'
import NewApp from '../pages/NewApp'
import AppReport from '../pages/AppReport'
import { Route, Switch, HashRouter } from 'react-router-dom'
export default class TheApp extends Component {
constructor(props) {
super(props)
}
render() {
const appId = window.location.href.split('id=')[1];
return (
<HashRouter>
<Switch>
<Route exact path="/" component={Cards} />
<Route path="/app" component={EditApp} />
<Route exact path="/new" component={NewApp} />
<Route path="/report" component={AppReport} />
</Switch>
</HashRouter>
)
}
}
The container where I dispatch the action
import { connect } from 'react-redux'
import Cards from './Cards'
import {
fetchAppsData
} from '../../../actions'
function mapStateToProps(state){
return {
apps: state.apps
}
}
function matchDispatchToProps(dispatch){
return dispatch(fetchAppsData)
}
export default connect(mapStateToProps, matchDispatchToProps)(Cards)
Action
import fetch from 'cross-fetch'
import * as helpers from '../Helpers';
export const REQUEST_ITEMS = 'REQUEST_ITEMS'
export const RECEIVE_ITEMS = 'RECEIVE_ITEMS'
export function fetchAppsData() {
return (dispatch) => {
return dispatch(fetchItems())
}
}
function fetchItems() {
return dispatch => {
dispatch(requestItems())
return fetch(helpers.appData)
.then(response => response.json())
.then(json => dispatch(receiveItems(json)))
}
}
function requestItems() {
return {
type: REQUEST_ITEMS
}
}
function receiveItems(json) {
return {
type: RECEIVE_ITEMS,
items: json,
receivedAt: Date.now()
}
}
The reducer
import {
REQUEST_ITEMS,
RECEIVE_ITEMS
} from '../actions/apps-actions'
export default function apps(
state = {
isFetching: false,
items: []
},
action
) {
switch (action.type) {
case REQUEST_ITEMS:
return Object.assign({}, state, {
isFetching: true
})
case RECEIVE_ITEMS:
return Object.assign({}, state, {
isFetching: false,
items: action.items
})
default:
return state
}
}
Try changing
function matchDispatchToProps(dispatch){
return dispatch(fetchAppsData)
}
Into this:
function matchDispatchToProps(dispatch){
return {
fetchAppsData: () => dispatch(fetchAppsData())
}
}
Also the function should be called “mapDispatchToProps” but that is not important for your problem.
I believe calling
dispatch(fetchAppsData)
isn't correct, fetchAppsData is a thunk creator, not a thunk directly. Instead you would want to do
dispatch(fetchAppsData())

Categories