I am using NextJS with next-i18next. This is my home page:
import {withTranslation} from '../config/next-i18next';
const Home = function Home() {
return (<div>test</div>)
};
Home.getInitialProps = async () => {
return {namespacesRequired: ['home']}
};
export default withTranslation('home')(Home);
What I want is to get current language inside a component/page, how can I do that ?
withTranslation injects the i18n object.
import {withTranslation} from '../config/next-i18next';
const Home = function Home({ i18n }) {
return (<div>{i18n.language}</div>)
// ----------------^
};
Home.getInitialProps = async () => {
return {namespacesRequired: ['home']}
};
export default withTranslation('home')(Home);
Or using Hooks,
import {useTranslation} from '../config/next-i18next';
const Home = function Home() {
const { i18n } = useTranslation('home');
return (<div>{i18n.language}</div>)
// ----------------^
};
Home.getInitialProps = async () => {
return {namespacesRequired: ['home']}
};
export default Home;
With Next.js you could also use the useRouter hook.
import {withTranslation} from '../config/next-i18next';
import { useRouter } from 'next/router'
const Home = function Home() {
const router = useRouter()
const currentLang = router.locale // => locale string eg. "en"
return (<div>test</div>)
};
Home.getInitialProps = async () => {
return {namespacesRequired: ['home']}
};
export default withTranslation('home')(Home);
Related
Laravel in PHP made this easy with https://laravel.com/docs/9.x/session#flash-data, so I figured Next.js would have an easy way too.
I thought I'd be able to do something like:
export const getServerSideProps: GetServerSideProps = async (ctx) => {
const session = await getSession(ctx);
if (!session) {
ctx.res.setHeader("yourFlashVariable", "yourFlashValue");
console.log('headers', ctx.res.getHeaders()); // Why is it not even appearing here?
return {
redirect: {
destination: '/',
permanent: false,
},
};
}
const props = ...
return { props };
};
and then in my other page:
export const getServerSideProps: GetServerSideProps = async (context) => {
const { headers, rawHeaders } = context.req;
// look inside the headers for the variable
// ...
But the header doesn't appear.
If you know how to achieve the goal of a flash variable (even if not using headers), I'm interested in whatever approach.
(Originally I asked How can I show a toast notification when redirecting due to lack of session using Next-Auth in Next.js? but now feel like I should have asked this more generic question.)
UPDATE
I appreciate the reasonable suggestion from https://stackoverflow.com/a/72210574/470749 so have tried it.
Unfortunately, index.tsx still does not get any value from getFlash.
// getFlash.ts
import { Session } from 'next-session/lib/types';
export default function getFlash(session: Session) {
// If there's a flash message, transfer it to a context, then clear it.
const { flash = null } = session;
console.log({ flash });
// eslint-disable-next-line no-param-reassign
delete session.flash;
return flash;
}
// getNextSession.ts
import nextSession from 'next-session';
export default nextSession();
// foo.tsx
import { getSession } from 'next-auth/react';
import { GetServerSideProps, InferGetServerSidePropsType, NextApiRequest, NextApiResponse } from 'next';
import getNextSession from '../helpers/getNextSession';
export const getServerSideProps: GetServerSideProps = async (ctx) => {
const session = await getSession(ctx);
if (!session) {
const req = ctx.req as NextApiRequest;
const res = ctx.res as NextApiResponse;
const nSession = await getNextSession(req, res);
nSession.flash = 'You must be logged in to access this page.'; // THIS LINE CAUSES A WARNING
console.log({ nSession });
return {
redirect: {
destination: '/',
permanent: false,
},
};
}
// ...
return { props };
};
// index.tsx
import { GetServerSideProps } from 'next';
import getFlash from '../helpers/getFlash';
import getNextSession from '../helpers/getNextSession';
export const getServerSideProps: GetServerSideProps = async (context) => {
const session = await getNextSession(context.req, context.res);
let toast = getFlash(session);
console.log({ toast });
if (!toast) {
toast = 'no toast';
}
console.log({ toast });
return {
props: { toast }, // will be passed to the page component as props
};
};
Also, the nSession.flash = line causes this warning:
warn - You should not access 'res' after getServerSideProps resolves.
Read more: https://nextjs.org/docs/messages/gssp-no-mutating-res
Your first code is working fine for me (printing the headers in terminal). However, the combination will not work as intended because the headers you set in /foo (say) will be sent to browser, along with a status code of 307, and a location header of /. Now "the browser" will be redirecting to the location and it won't forward your headers. Similar threads: https://stackoverflow.com/a/30683594, https://stackoverflow.com/a/12883411.
To overcome this, you can do something like this. This works because the browser does send the cookies (in this case, set when you create a session).
// lib/session.ts
import type { IronSessionOptions } from 'iron-session'
import type { GetServerSidePropsContext, GetServerSidePropsResult, NextApiHandler } from 'next'
import { withIronSessionApiRoute, withIronSessionSsr } from 'iron-session/next'
export const sessionOptions: IronSessionOptions = {
password: process.env.SECRET_COOKIE_PASSWORD as string,
cookieName: 'sid',
cookieOptions: { secure: process.env.NODE_ENV === 'production' },
}
declare module 'iron-session' {
interface IronSessionData {
flash?: string | undefined
}
}
export const withSessionRoute = (handler: NextApiHandler) =>
withIronSessionApiRoute(handler, sessionOptions)
export const withSessionSsr = <P extends Record<string, unknown> = Record<string, unknown>>(
handler: (
context: GetServerSidePropsContext
) => GetServerSidePropsResult<P> | Promise<GetServerSidePropsResult<P>>
) => withIronSessionSsr(handler, sessionOptions)
// pages/protected.tsx
import type { NextPage } from 'next'
import { getSession } from 'next-auth/react'
import { withSessionSsr } from 'lib/session'
const ProtectedPage: NextPage = () => <h1>Protected Page</h1>
const getServerSideProps = withSessionSsr(async ({ req, res }) => {
const session = await getSession({ req })
if (!session) {
req.session.flash = 'You must be logged in to access this page.'
await req.session.save()
return { redirect: { destination: '/', permanent: false } }
}
return { props: {} }
})
export default ProtectedPage
export { getServerSideProps }
// pages/index.tsx
import type { InferGetServerSidePropsType, NextPage } from 'next'
import { withSessionSsr } from 'lib/session'
const IndexPage: NextPage<InferGetServerSidePropsType<typeof getServerSideProps>> = ({ flash }) => {
// TODO: use `flash`
}
const getServerSideProps = withSessionSsr(async ({ req }) => {
// if there's a flash message, transfer
// it to a context, then clear it
// (extract this to a separate function for ease)
const { flash = null } = req.session
delete req.session.flash
await req.session.save()
return { props: { flash } }
})
export default IndexPage
export { getServerSideProps }
This also works if you want to set flash data in an API route instead of pages:
import { withSessionRoute } from 'lib/session'
const handler = withSessionRoute(async (req, res) => {
req.session.flash = 'Test'
await req.session.save()
res.redirect(307, '/')
})
export default handler
Complete example: https://github.com/brc-dd/next-flash/tree/with-iron-session
Hello I have custom hook
code is like below
import * as React from 'react'
export const myCustomHook = (props?: boolean) => {
const [value, setValue] = React.useState([]);
React.useEffect(() => {
return (async (p1) => {
// ....
setValue(someValues)
})
}, [])
const myFun = async (prop1) => {
// ... some operations
return {id: id}
}
return { myFun, value }
}
I am using the above like this
const { value, myFun } = myCustomHook();
const foofun = async (pp) => {
const myfunRes = await myFun(prop1);
}
Now I want to put myFun in useEffect
Please help me with this.
When I tried to execute following test code, I got error like "The first character of a path should be / or *"
archives.test.ts
import ...
describe('Get All Archived', () => {
test('it must return ok', async () => {
const app = init()
const response = await app.inject().get('/client/archives')
const body = JSON.parse(response.body)
expect(response.statusCode).toBe(200)
expect(body.status).toBe('ok')
expect(body.data.length).toBe(0)
})
})
My code:
api.ts
import fastify, { FastifyInstance } from 'fastify'
other imports...
export const init = (): FastifyInstance => {
const app = fastify()
app.addContentTypeParser('application/json', { parseAs: 'string' }, parseApplicationJson)
app.register(ArchivesPlugin)
return app
}
const proxy = awsLambdaFastify(init())
export const handler = proxy
ArchivesPlugin.ts
import fp from 'fastify-plugin'
import { FastifyInstance, FastifyPluginAsync } from 'fastify'
import { register } from '../controllers/archives'
const ArchivesPlugin: FastifyPluginAsync = async (app: FastifyInstance) => {
await register(app)
}
export default fp(ArchivesPlugin)
register.ts
import { FastifyInstance, FastifyRequest } from 'fastify'
other imports...
const handlers = {
code content
},
}
export async function register(app: FastifyInstance) {
app.route({
method: 'GET',
url: `${prefix}/archives`,
schema: { ...GetArchiveListResponse, tags: ['archives'] },
handler: handlers.index,
})
}
Appreciate any help.
The problem was caused by ${prefix} in register.ts which is given above. When called register.ts, prefix seemed undefined. That's why I was getting the error.
i'm currently using NodeJS.
I'm trying to import a module to a component function and everything executes pretty well, but i still get this error in the server console:
error - src\modules\accountFunctions.js (15:35) # Object.User.GetData
TypeError: _cookieCutter.default.get is not a function
cookieCutter.get is actually a function and is working as inteneded
import cookieCutter from 'cookie-cutter'
import { useSelector, useDispatch } from 'react-redux'
import { useRouter } from 'next/router'
import { accountActions } from '../store/account'
const Auth = require('./auth.module')
const User = {}
User.GetData = async () => {
const route = useRouter()
const userData = useSelector((state) => state.user)
const dispatch = useDispatch()
const sessionId = cookieCutter.get('session')
if (sessionId && userData.username === '') {
const userExist = await Auth.loadUserInformation()
if (userExist.result === false) {
route.push('/login')
return false
}
dispatch(accountActions.updateAccountInformation(userExist.data))
return true
} else if (!sessionId) {
route.push('/login')
return false
}
}
module.exports = User
I know for a fact that a solution would be importing the library into the function compoenent but i really don't wanna keep on importing it everywhere.
This is how i'm importing the module.
import User from '../src/modules/accountFunctions'
const dashboard = () => {
console.log('Rance')
User.GetData()
return <NavBar />
}
export default dashboard
You need to move the cookie fetching logic to a useEffect inside the custom hook, so it only runs on the client-side. Calling cookieCutter.get won't work when Next.js pre-renders the page on the server.
const useUserData = async () => {
const route = useRouter()
const userData = useSelector((state) => state.user)
const dispatch = useDispatch()
useEffect(() => {
const getAuthenticatedUser = async () => {
const sessionId = cookieCutter.get('session')
if (sessionId && userData.username === '') {
const userExist = await Auth.loadUserInformation()
if (userExist.result === false) {
route.push('/login')
}
dispatch(accountActions.updateAccountInformation(userExist.data))
} else if (!sessionId) {
route.push('/login')
}
}
getAuthenticatedUser()
}, [])
}
I'm trying to implement Bullet train API in a React web app. According to their node client documentation, I have setup the following function:
export const isFeatureEnabled = async (nameOfTheFeature) => {
return new Promise((resolve) => {
bulletTrain.init({
environmentID: BULLET_TRAIN_ENV_ID
});
bulletTrain.hasFeature(nameOfTheFeature)
.then((featureFlag) => {
if (featureFlag[nameOfTheFeature].enabled) {
resolve(true);
}
})
.catch(err => resolve(false));
});
}
This is called in regular components like this:
render() {
return (<div>{await isFeatureEnabled('feature1') && <p>feature1 is enabled</p>}</div>)
};
which throws this:
Parsing error: Can not use keyword 'await' outside an async function
If we add the async keyword, with a proper return statement:
async render() {
return (<div>{await isFeatureEnabled('feature1') && <p>feature1 is enabled</p>}</div>)
};
Then it throws:
Your render method should have return statement
So what is the correct way to use this promised function inside a react app?
I would suggest you not to use await keyword in render instead use componentDidMount and constructor for this and use state object to check:
constructor(props){
super(props);
this.state = { isFeatEnabled: false };
}
componentDidMount(){
this.setState({isFeatEnabled:isFeatureEnabled('feature1')})
}
Now in the render:
render() {
return (<div>{this.state.isFeatEnabled && <p>feature1 is enabled</p>}</div>)
};
And remove the async from the method.
call function isFeatureEnabled inside an async function during mount (before/after your wish)
example -
export const isFeatureEnabled = async (nameOfTheFeature) => {
return new Promise((resolve) => {
bulletTrain.init({
environmentID: BULLET_TRAIN_ENV_ID
});
bulletTrain.hasFeature(nameOfTheFeature)
.then((featureFlag) => {
if (featureFlag[nameOfTheFeature].enabled) {
resolve(true);
}
})
.catch(err => resolve(false));
});
}
...
componentDidMount() {
this.checEnabled();
}
...
const checkEnabled = async () => {
const flag = await isFeatureEnabled('feature1');
this.setState({f1enabled: flag});
}
...
render() {
return (<div>{this.state.f1enabled ? <p>feature1 is enabled</p> : null}</div>)
}
If isFeatureEnabled is in the same file keep it outside class component or else keep it in another file and export the function.
You can't use promise at there, the proper way:
import React, { useEffect, useState } from 'react'
import bulletTrain from '../somewhere'
import BULLET_TRAIN_ENV_ID from '../somewhere'
export default function featureComponent({ featureName }) {
const [featureEnabled, setFeatureEnabled] = useState(false)
useEffect(() => {
bulletTrain.init({
environmentID: BULLET_TRAIN_ENV_ID
})
bulletTrain
.hasFeature(featureName)
.then(featureFlag => {
if (featureFlag[featureName].enabled) {
setFeatureEnabled(true)
}
})
.catch(err => setFeatureEnabled(false))
}, [featureName])
return <div>{featureEnabled && <p>{featureName} is enabled</p>}</div>
}
Append isFeatureEnabled function re-use answer below:
import React, { useEffect, useState } from 'react'
import isFeatureEnabled from '../somewhere'
export default function featureComponent({ featureName }) {
const [featureEnabled, setFeatureEnabled] = useState(false)
useEffect(() => {
const checkAndSetEnabled = async () => {
const enabled = await isFeatureEnabled(featureName)
setFeatureEnabled(enabled)
}
checkAndSetEnabled()
}, [featureName])
return <div>{featureEnabled && <p>{featureName} is enabled</p>}</div>
}