Use variables (or store) in all page/component, in nuxt3 - nuxtjs3

I am using nuxt3, pinia.
I can use the user, is_login variables in a specific vue page, as seen below.
import { useAuthStore } from "~/stores/myCustomAuthStore";
import { storeToRefs } from 'pinia'
const authStore = useAuthStore();
const {user, is_login} = storeToRefs(authStore)
What I want is to use the user, is_login variables in another page (or component) without writing the 4 lines of code above.
I think I need to use a plugin or module or nuxtApp.provide, how should I do it detaily?
------ what i tried is -------
I made plugins/common.ts
import { useAuthStore } from "~/stores/myCustomAuthStore";
import { storeToRefs } from 'pinia'
export default defineNuxtPlugin((nuxtApp) => {
const authStore = useAuthStore();
const {user, is_login} = storeToRefs(authStore)
nuxtApp.provide('user', user.value)
nuxtApp.provide('is_login', is_login.value)
}
and I put below code every
const is_login = useNuxtApp().$is_login
const user = useNuxtApp().$user
This is not work.

You can write a composable for this (see https://nuxt.com/docs/guide/directory-structure/composables#composables-directory):
Create a composables/use-auth.ts file in the "composables" directory
// composables/use-auth.ts
import { useAuthStore } from '~/stores/myCustomAuthStore';
import { storeToRefs } from 'pinia';
export const useAuth = () => {
const pinia = usePinia();
const authStore = useAuthStore(pinia);
const { user, is_login } = storeToRefs(authStore);
return {
user,
is_login,
};
}
Then in your component you can use it like:
<script setup>
const { user, is_login } = useAuth();
</script>

Related

Next.js: How to use useState and AuthContext without invalidating SSG html

I have an AuthContext to manage the authentication in my Next.js app. The _app.js file looks like this:
import '../styles/global.css'
import 'tailwindcss/tailwind.css'
import { AuthProvider } from '../components/AuthContext'
function MyApp({ Component, pageProps }) {
return (
<>
<AuthProvider>
<Component {...pageProps} />
</AuthProvider>
</>
)
}
export default MyApp
And the AuthContext file is something like this:
import React, { useContext, useState, useEffect } from "react"
import { auth, db } from "./Firebase";
import { doc, getDoc } from "firebase/firestore";
const AuthContext = React.createContext()
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState([])
const [loading, setLoading] = useState(true)
const [userData, setUserData] = useState({})
const [companyData, setCompanyData] = useState({})
useEffect(() => {
const unsubscribe =
auth.onAuthStateChanged(user => {
setCurrentUser(user)
setLoading(false)
})
return unsubscribe
}, [])
useEffect(() => {
const fetchData = async () => {
const qu = doc(db, "users", currentUser.uid)
const datau = await getDoc(qu)
setUserData(datau.data());
const qc = doc(db, "companies", currentUser.uid)
const datac = await getDoc(qc)
setCompanyData(datac.data());
}
currentUser?.uid && fetchData()
}, [currentUser])
const value = {
currentUser,
userData,
companyData
}
return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
)
}
export function useAuth() {
return useContext(AuthContext)
}
Then I have a dynamic page [id].js that is something like this:
import { useAuth } from '../../components/AuthContext';
import Head from 'next/head';
export async function getStaticProps(context) {
// fetch and return data
}
export async function getStaticPaths() {
// fetch paths
return { paths, fallback: 'blocking' }
}
export default function Job(props) {
const { userData, currentUser } = useAuth();
const data = props.data;
return (
<>
<Head>
{data.title} | SiteName
</Head>
<h1>data.title</h1>
{currentUser ? userData.name : null}
</>
)
}
Problem: The website works perfectly, BUT when I check the page [id].js source code, Next.js doesn't build the HTML structure with head and body optimized for SEO (which is the main reason why I'm migrating to Next.js).
If I remove the "!loading &&" (see below) in the AuthContext file, the Next's SSG HTML generation works BUT the whole app starts to give me errors everywhere.
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
What exactly is the problem? And any idea on how to solve it?

I have successfully implemented the redux-persist with next-redux-wrapper in next js

Im getting data from the external api and storing it in the reducer.And im using redux-persist to persist the state while navigating from one page to another.But i have made left the whiteList as an empty array but all the state are being persisted?Need help
import "../assets/css/style.scss";
import "owl.carousel/dist/assets/owl.carousel.css";
import "owl.carousel/dist/assets/owl.theme.default.css";
import Layout from "../component/Layout/Layout";
import { wrapper } from "../redux/store";
import { useEffect } from "react";
import { useStore } from "react-redux";
function MyApp({ Component, pageProps }) {
const store = useStore((store) => store);
useEffect(() => {
{
typeof document !== undefined
? require("bootstrap/dist/js/bootstrap.bundle")
: null;
}
}, []);
return (
<Layout>
<Component {...pageProps} />;
</Layout>
);
}
export default wrapper.withRedux(MyApp);
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";
import { persistStore, persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage";
import rootReducer from "./index";
import { createWrapper, HYDRATE } from "next-redux-wrapper";
const middleware = [thunk];
let initialState={}
// BINDING MIDDLEWARE
const bindMiddleware = (middleware) => {
if (process.env.NODE_ENV !== "production") {
return composeWithDevTools(applyMiddleware(...middleware));
}
return applyMiddleware(...middleware);
};
const makeStore = ({ isServer }) => {
if (isServer) {
//If it's on server side, create a store
return createStore(rootReducer,initialState, bindMiddleware(middleware));
} else {
//If it's on client side, create a store which will persis
const persistConfig = {
key: "root",
storage: storage,
whiteList: [],
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = createStore(persistedReducer,initialState, bindMiddleware(middleware));
store.__persisitor = persistStore(store); // This creates a persistor object & push that
persisted object to .__persistor, so that we can avail the persistability feature
return store;
}
};
// export an assembled wrapper
export const wrapper = createWrapper(makeStore);
If you keep the whitelist an empty array then nothing will be persisted.
You have to put inside that string array the redux reducers values you want to be persisted.
Example:
const persistConfig = {
key: 'root',
storage: storage,
whiteList: ['cart', 'form', 'user'],
};

How to add firebase screen tracking analytics with React Native React Navigation when using createStackNavigator?

I have a React Native app and am using React Navigation. I am now trying to add screen tracking analytics with firebase.
I am following this documentation, which has this sample code:
import analytics from '#react-native-firebase/analytics';
import { NavigationContainer } from '#react-navigation/native';
<NavigationContainer
ref={navigationRef}
onStateChange={state => {
const previousRouteName = routeNameRef.current;
const currentRouteName = getActiveRouteName(state);
if (previousRouteName !== currentRouteName) {
analytics().setCurrentScreen(currentRouteName, currentRouteName);
}
In my code, however, I am creating my base NavigationContainer with a function like so:
export default createStackNavigator(
{
Home: MainTabNavigator,
SignIn: SignInNavigator,
},
{
transitionConfig: dynamicModalTransition,
headerMode: 'none',
initialRouteName: 'Home',
},
);
What is the best way to integrate the code from the example?
The problem is because you are on react-navigation v4.x.x, but the example you have is for v5.x.x.
In v4, event listeners can be added on AppContainer.
The example below is for v4.
import React from 'react';
import { createAppContainer, createStackNavigator } from 'react-navigation';
function getActiveRouteName(navigationState) {
if (!navigationState) {
return null;
}
const route = navigationState.routes[navigationState.index];
if (route.routes) {
return getActiveRouteName(route);
}
return route.routeName;
}
const nav = createStackNavigator({...});
const AppContainer = createAppContainer(nav);
export default () => {
return <AppContainer
onNavigationStateChange={(prevState, currentState, action) => {
const currentRouteName = getActiveRouteName(currentState);
const previousRouteName = getActiveRouteName(prevState);
if (previousRouteName !== currentRouteName) {
analytics().setCurrentScreen(currentRouteName, currentRouteName);
}
}}
/>
}
I'm using NavigationContainer and createStackNavigator, too and this is how I did it, like in the example for screen tracking at reactnavigation.org
import * as Analytics from 'expo-firebase-analytics';
import { useRef } from 'react';
import { NavigationContainer } from '#react-navigation/native';
export default () => {
const navigationRef = useRef();
const routeNameRef = useRef();
return (
<NavigationContainer
ref={navigationRef}
onReady={() =>
(routeNameRef.current = navigationRef.current.getCurrentRoute().name)
}
onStateChange={async () => {
const previousRouteName = routeNameRef.current;
const currentRouteName = navigationRef.current.getCurrentRoute().name;
if (previousRouteName !== currentRouteName) {
// The line below uses the expo-firebase-analytics tracker
// https://docs.expo.io/versions/latest/sdk/firebase-analytics/
// Change this line to use another Mobile analytics SDK
await analytics().logScreenView({
screen_name: currentRouteName,
screen_class: currentRouteName
});
}
// Save the current route name for later comparison
routeNameRef.current = currentRouteName;
}}
>
{/* ... */}
</NavigationContainer>
);
};

Redux state don't change after dispatch on next js

I am new on next js and i use redux-next-wrapper !
The problem is that i want to dispatch a token access but when i am doing it in getInitialProps, the store does not update after the render of the page !
I try to use componentDidMount, it work, but the state is update only after the render of the page, which make visible the button login one second before to be replace by logout !
componentDidMount () {
const token_access = Cookies.get('xxxxxxxxxx');
const token_refresh = Cookies.get('xxxxxxxxxx');
console.log(token_access);
if (token_access && token_refresh) {
const decode = jwt_decode(token_refresh);
if (decode.exp >= new Date()) {
this.props.store.dispatch(logout(token_refresh))
}
else {
const decode_access = jwt_decode(token_access);
if (decode_access.exp >= new Date()) {
refresh(token_refresh)
}
this.props.store.dispatch(userLoggedIn(token_access));
}
}
}
static async getInitialProps ({Component, ctx}) {
const token = '12345';
ctx.store.dispatch(userLoggedIn(token));
return {
pageProps: (Component.getInitialProps ? await Component.getInitialProps(ctx) : {})
}
}
import { createStore, applyMiddleware } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import rootReducer from '../reducers/index'
export default initialState => createStore(
rootReducer,
composeWithDevTools(
applyMiddleware(thunk)
)
);
is there a way to dispatch and load the store before the render of the page ?
Thank you for your answer
I fix this issue by changing the store ! Hope this help someone :)
export const initStore = (initialState = {} ) => {
return createStore(rootReducer,
initialState, composeWithDevTools(applyMiddleware(thunk)))
};

Next.js withRouter not always updating the page

I have a problem with my next.js app. It seems that when the same component gets rendered twice, it does not update the url properly.
I have a main component wrapper that goes around everything:
import React from "react"
import "babel-polyfill"
import root from "window-or-global"
import { withRouter } from "next/router"
import commonApi from "./../../common/common"
import AppLayout from "./../../components/main/AppLayoutWrapper"
import configureStore from "./../../redux/configureStore"
import routes from "./../../../server/routes/routes"
const { makeWithRedux } = configureStore
const { Router } = routes
const { getRequestAreaInformation } = commonApi
let store = null
const MainWrapper = (Page: React$Node) => {
const PageWrapper = class PageWrapper extends React.Component<{}> {
static async getInitialProps(req: any) {
return getRequestAreaInformation(req)
}
// Required to make the components work on page load, but fragile
componentDidMount() {
this.props.actions.urlLocationChanged(this.props.router)
}
render() {
this.props.actions.urlLocationChanged(this.props.router)
if (Page.getInitialProps) {
Promise.resolve(Page.getInitialProps({ ...this.props })).then(() => {})
}
if (AppLayout.getInitialProps) {
Promise.resolve(AppLayout.getInitialProps({ ...this.props })).then(() => {})
}
return (
<section className="main-wrapper-jsx">
<AppLayout {...this.props}>
<Page {...this.props} />
</AppLayout>
</section>
)
}
}
return withRouter(makeWithRedux(PageWrapper))
}
export default MainWrapper
The routes file links up to this:
const link = require("next/link")
const router = require("next/router")
const NextRouter = router.default || router
const NextLink = link.default || link
const routes = require("next-routes")({
Router: NextRouter,
Link: NextLink,
})
const MediaListView = "MediaListView"
const AuthView = "auth/AuthView"
const LibraryView = "LibraryView"
routes
.add("root", "/", LibraryView)
.add("home", "/home", LibraryView)
.add("search", "/search", MediaListView)
.add("login", "/login", AuthView)
.add("register", "/register", AuthView)
module.exports = routes
Then finally, there's the redux store that implements this action.
import { handleActions, createAction } from "redux-actions"
import { fromJS, toJS } from "immutable"
import root from "window-or-global"
const initialState = fromJS({
asPath: "",
pathname: "",
query: {},
route: "",
})
const urlLocationChanged = createAction(`${routeParams.URL_LOCATION_CHANGED}`, (url) => {
return url
})
const actions = {
urlLocationChanged,
}
const routeActions = handleActions(
{
URL_LOCATION_CHANGED: (state, {payload}) => {
const location = payload
const pathname = decodeURIComponent(location.asPath.slice(1))
let param = pathname
if (param && param.match(/\w+\D/)) {
param = param.match(/\w+\D/)[0]
}
return state
.set("location", location)
}
return state.set("location", location)
},
},
initialState,
)
export default {
initialState,
routeActions,
actions,
}
All of this should work, but whenever there's two clicks on the same object, let's say someone clicks on login and then clicks on register, nothing happens.

Resources