I am working on a small project where I created a provider to change the language from Arabic to English and vice versa. Here is how it looks:
import React from "react";
import { useSelector } from "react-redux";
import { useTranslation } from "react-i18next";
const LanguageProvider = ({ children }) => {
const lang = useSelector((state) => state.LanguageReducer);
const { i18n } = useTranslation();
React.useEffect(() => {
if (i18n.changeLanguage) i18n.changeLanguage(lang.language);
}, [i18n, lang]);
return <div {...{ dir: lang.activeDir }}>{children}</div>;
};
export default LanguageProvider;
And I wrap my app with this provide along with the context provider. But I face this issue when the home page of the project loads and I cannot solve it so far.
React has detected a change in the order of Hooks called by LanguageProvider. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks
What could the problem be here?
Related
Next13 was released a week ago, and I am trying to migrate a next12 app to a next13.
I want to use server-side components as much as possible, but I can't seem to use
import { createContext } from 'react';
in any server component.
I am getting this error:
Server Error
Error:
You're importing a component that needs createContext. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
,----
1 | import { createContext } from 'react';
: ^^^^^^^^^^^^^
`----
Maybe one of these should be marked as a client entry with "use client":
Is there an alternative here or do I have to resort to prop drilling to get server-side rendering?
It seems like I can use createServerContext
import { createServerContext } from 'react';
If you're using Typescript and React 18, you'll also need to add "types": ["react/next"] to your tsconfig.json compiler options, since this is a not-yet-stable function.
This is a new feature from React's SSR to recognize whether a component is client-side or server-side. In your case, createContext is only available on the client side.
If you only use this component for client-side, you can define 'use client'; on top of the component.
'use client';
import { createContext } from 'react';
You can check this Next.js document and this React RFC for the details
According to Next.js 13 beta documentation, you cannot use context in Server Components:
In Next.js 13, context is fully supported within Client Components, but it cannot be created or consumed directly within Server Components. This is because Server Components have no React state (since they're not interactive), and context is primarily used for rerendering interactive components deep in the tree after some React state has been updated
However, there are alternative ways to handle data in the new approach, depending on your case. F.e. if you fetched the data from the server in a parent component and then passed it down the tree through Context, you can now fetch the data directly in all the components that depend on this data. React 18 will dedupe (de-duplicate) the fetches, so there are no unnecessary requests.
There are more alternatives in the documentation.
I've made a tiny package to handle context in server components, works with latest next.js, it's called server-only-context:
https://www.npmjs.com/package/server-only-context
Usage:
import serverContext from 'server-only-context';
export const [getLocale, setLocale] = serverContext('en')
export const [getUserId, setUserId] = serverContext('')
import { setLocale, setUserId } from '#/context'
export default function UserPage({ params: { locale, userId } }) {
setLocale(locale)
setUserId(userId)
return <MyComponent/>
}
import { getLocale, getUserId } from '#/context'
export default function MyComponent() {
const locale = getLocale()
const userId = getUserId()
return (
<div>
Hello {userId}! Locale is {locale}.
</div>
)
}
This is the code for it, it's really simple:
import 'server-only'
import { cache } from 'react'
export default <T>(defaultValue: T): [() => T, (v: T) => void] => {
const getRef = cache(() => ({ current: defaultValue }))
const getValue = (): T => getRef().current
const setValue = (value: T) => {
getRef().current = value
}
return [getValue, setValue]
}
I've been trying to use highlight.js with my react project , but it never worked.
I have a blog, and for each post I fetch the post data from the server.
Unfortunately - highlight.js does not work properly when it comes to dynamic data and async proccess.
Lets say we have this code that i fetch:
bla blba blahbla blba blahbla blba blahbla blba blah...
<code><div class="test"></div></code>
bbla blba blah....
So , I did try to import highlight.js with the css theme, and used hljs.highlightAll() , but it didn't work properly.
Maybe there is some other solution \ library where I can highlight the code?
What's the alternative?
thank you.
import { React, useEffect } from "react";
import { useParams } from "react-router-dom";
import hljs from "../../node_modules/highlight.js/lib/core";
import "../../node_modules/highlight.js/styles/arta.css";
import "./BlogPost.css";
function BlogPost(props) {
hljs.initHighlightingOnLoad();
const updateCodeSyntaxHighlighting = () => {
document.querySelectorAll("code").forEach((block) => {
hljs.highlightBlock(block);
});
};
useEffect(() => {
getPostData();
updateCodeSyntaxHighlighting();
}, []);
// GetPostDataById(id);
return <div dangerouslySetInnerHTML={{ __html: postData }}></div>;
}
export default BlogPost;
result:
as you can see - the background gets darker , but not really highlighted text.
Your not importing the entire highlight.js library so you can just do import hljs from 'highlight.js' instead of going into the node modules folder to get it. Also you are not specifying the language you want to highlight in with the class of the code block. In the example I am just going to hard code the postData with class='language-javascript' for the <code> block to get the correct syntax highlighting. So here is a working example:
Working Codesandbox
import { React, useEffect } from 'react'
import hljs from 'highlight.js'
import '../../node_modules/highlight.js/styles/arta.css'
const postData = `<pre><code class="language-javascript">console.log('hello');</code></pre>`
function BlogPost(props) {
const updateCodeSyntaxHighlighting = () => {
document.querySelectorAll('code').forEach((block) => {
hljs.highlightElement(block)
})
}
useEffect(() => {
updateCodeSyntaxHighlighting()
}, [])
// GetPostDataById(id);
return <div dangerouslySetInnerHTML={{ __html: postData }}></div>
}
I am building some web pages in nextjs and I need to make sure certain pages can only be accessed if the user has been authenticated as below:
import UserManager from "../managers/user_manager";
import { useRouter } from "next/router";
import LogInPage from "../pages/auth/login";
export default function EnsureAuthenticated(OriginalComponent) {
const router = useRouter();
const loggedInUser = UserManager.getLoggedInUser();
if (loggedInUser ) {
return <OriginalComponent />;
}
return router.push(LogInPage.routePath);
}
And here is my dashboard page that I need to wrap with the above HOC to enforce authentication
import { useEffect } from "react";
import { useRouter } from "next/router";
import EnsureAuthenticated from "../../components/auth_hoc";
function DashboardHomePage(props) {
const router = useRouter();
return (<div>
Home Page
</div>);
}
DashboardHomePage.routePath = "/dashboard/home";
export default EnsureAuthenticated();
Unfortunately for me I keep getting this error after compiling in NextJS
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
Please, how do I resolve this?
Thank you.
You aren't passing DashboardHomepage component to HOC .
HOC code has to changed , we cannot use hooks in HOC body because we are calling HOC as a function instead of a component. When we call a component as a function react excludes all the lifecycles from that.
There is also an issue with the usage of router in your code, as in nextjs if we use it in render it will be rendered and client side and server side.
You may have to create a component which will do routing in useEffect and use it instead of router.push(LogInPage.routePath)
import React from "react";
import UserManager from "../managers/user_manager";
import { useRouter } from "next/router";
import LogInPage from "../pages/auth/login";
export default function EnsureAuthenticated(OriginalComponent) {
return (props) => {
const router = useRouter();
const loggedInUser = UserManager.getLoggedInUser();
if (loggedInUser ) {
return <OriginalComponent />;
}
//below code can give error as router is supposed to be called only at client side, you might have to implement it using useEffect
return router.push(LogInPage.routePath);
};
}
import { useRouter } from "next/router";
import EnsureAuthenticated from "../components/test/AddCount";
function DashboardHomePage(props) {
const router = useRouter();
return <div>Home Page</div>;
}
DashboardHomePage.routePath = "/dashboard/home";
export default EnsureAuthenticated(DashboardHomePage);
I do use recoil in my nextjs application.
But if I run next (in dev or production make no difference) I got this error-message:
Duplicate atom key "companyData". This is a FATAL ERROR in
production. But it is safe to ignore this warning if it occurred because of
hot module replacement.
This is the way I've implemented it:
/src/stores/CompanyStore.js:
import { useSetRecoilState, useRecoilValue , atom } from 'recoil';
import config from '../../content/config.yml';
const companyData = atom({
key: 'companyData',
default: {...config.company},
});
export const useSetCompanyData = () => useSetRecoilState(companyData);
export const useCompanyData = () => useRecoilValue(companyData);
export default {
useSetCompanyData,
useCompanyData,
};
I use it like this in some components:
MyComponent.js
import React from 'react';
...
...
import {useCompanyData} from '../stores/CompanyStore';
const MyComponent = () => {
const classes = useStyles();
const companyData = useCompanyData();
const { summary: headline, description } = companyData;
return (<div><h2>{headline}</h2><p>{description}</p>)
I don't see, why this error-message appears. Might it caused of a bug in nextjs, or did I implement recoil in a wrong way?
Looks like a problem with recoil in nextjs when you have state in a separate file:
https://github.com/facebookexperimental/Recoil/issues/733
As of Recoil 0.7.6, add RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED=false to your environment to hide these warnings.
(roeland had the correct GitHub issue, which has since been closed)
I'm trying to build a React web app (v 16.13.0). I want a flash message component, to display status after a form is submitted. If there is already something standard, that would be preferable, but since I can't find anything, I'm trying to roll my own using this -- https://medium.com/#jaouad_45834/building-a-flash-message-component-with-react-js-6288da386d53 . Here's the component
import React, { useEffect, useState } from 'react';
import Bus from '../Utils/Bus';
import './index.css';
export const Flash = () => {
let [visibility, setVisibility] = useState(false);
let [message, setMessage] = useState('');
let [type, setType] = useState('');
useEffect(() => {
Bus.addListener('flash', ({message, type}) => {
setVisibility(true);
setMessage(message);
setType(type);
setTimeout(() => {
setVisibility(false);
}, 4000);
});
}, []);
useEffect(() => {
if(document.querySelector('.close') !== null) {
document.
querySelector('.close').
addEventListener('click', () => setVisibility(false));
}
})
return (
visibility && <div className={`alert alert-${type}`}>
<span className="close"><strong>X</strong></span>
<p>{message}</p>
</div>
)
}
Problem is, web site uses custom code, but doesn't show source for
Bus.addListener('flash', ({message, type}) => {
setVisibility(true);
setMessage(message);
setType(type);
setTimeout(() => {
setVisibility(false);
}, 4000);
});
so my question is, how do I add an event listener to a React component?
Edit: In response to the answer given, I created this file ...
localhost:client davea$ cat src/Utils/Bus.js
import EventEmitter from 'events';
export default new EventEmitter();
but re-compiling my module results in this error ...
./src/components/Flash/index.js
Module not found: Can't resolve '../Utils/Bus' in '/Users/davea/Documents/workspace/chicommons/maps/client/src/components/Flash'
These are the first lines of the file above. Note the second "import" where I'm importing "Bus" ...
import React, { useEffect, useState } from 'react';
import Bus from '../Utils/Bus';
import './index.css';
export const Flash = () => {
The website included the code: https://medium.com/#jaouad_45834/building-a-flash-message-component-with-react-js-6288da386d53
To set that up, we need to create a new folder in our root directory and name it Utils and create on it a Bus.js file with will contains the following code:
import EventEmitter from 'events';
export default new EventEmitter();
This is all Bus.js is, a simple event emitter.
You can also use react-toastify or useToasts for this.
In order to show the flash message, you need to do something like the following.
Bus.emit('flash', {type: 'danger', message: 'error occurred'});
I have used the code you have provided and mixed it with a dummy form. Upon submitting the form, the flash message appears successfully.
Live example & working code is here:
https://codesandbox.io/s/dazzling-lamarr-k3cn5
Some notes:
I have refactored and removed 2nd useEffect as it is inefficient and unnecessary. The onClick can very well be applied to the close-span-element
If you are using redux, you can use create global Flash/Alert message and connect it to the store. Any redux-connected-component can simply work its own logic and dispatch an alert action and render different types of messages.
Using ready made libraries are also cool.
I think what you want is widely known as a Toaster, and you can use a library like React Toastify [https://github.com/fkhadra/react-toastify] with a simple configuration and high customization