Use SurveyJS along with Nextjs - reactjs

As knows NextJS is using the server side rendering, I want to use SurveyJs with it but surveyJS is using some function that must be executed in client side. In my case I wanna use StylesManager.applyTheme but it throws an server error of ReferenceError: document is not defined
any possible way can I use to execute the applyTheme function in client side?

You can achieve this creating a useEffect Hook that consumes an external function to apply this theme:
import {useEffect} from 'react'
useEffect(() => {
const newSurveyCreator = new SurveyJSCreator.SurveyCreator('surveyCreatorContainer', surveyCreatorConfig);
newSurveyCreator.saveSurveyFunc = function() {
console.log(this);
console.log(this && this.text);
};
updateSurveyCreator(newSurveyCreator);
}, []);

For better implementation, I solved the issue using dynamic imports from NextJs
Instead, I imported my SurveyComponent like this:
const SurveyComponent = dynamic(
() => import("../../../../components/survey/PartOne"),
{
ssr: false,
}
);
To make sure my SurveyComponent does not render on server-side.

Related

How I can get localstorage data inside getServerSideProps

I am using NextJS 12. I am trying to get local storage object. When I use localstorage inside getServerSideProps I get an error like this ReferenceError: localStorage is not defined. I tried to use it outside the function as well but I still get this error. Is there any way to use it inside getServerSideProps.
export async function getServerSideProps({ query }) {
const id = query.id;
const getData = JSON.parse(localStorage.getItem("form"));
console.log(getData)
return {
props: {},
}
Welcome to StackOverflow, as it refers in the documentation
If you export a function called getServerSideProps (Server-Side Rendering) from a page, Next.js will pre-render this page on each request using the data returned by getServerSideProps.
Localstorage is only available on the client side and you are trying to access it in a server side only function , you can use something like
if (typeof window !== 'undefined') {
// your code
const id = query.id;
const getData = JSON.parse(localStorage.getItem("form"));
console.log(getData)
}
Please review this article to get more information on running client side only code.
Another approach would be to use a dynamic import where the hello3 component would contain the code accessing local storage.
import dynamic from 'next/dynamic'
const DynamicComponentWithNoSSR = dynamic(
() => import('../components/hello3'),
{ ssr: false }
)
function Home() {
return (
<div>
<Header />
<DynamicComponentWithNoSSR />
<p>HOME PAGE is here!</p>
</div>
)
}
export default Home
Another way would be using cookies instead of using localstorage, I had the same problem when I developed my last application and I solved it using the nookies package
Nookies: A collection of cookie helpers for Next.js

How to import a function in Next.js only on client side?

I am very new to Next.js, so pardon me for my mistakes. I have this function written using in axios that I only want to run on client side, since it used localstorage. Dynamic imports only work for components. Is there a way I could do that for just a function?
It sounds like you're doing the data fetching on the client side. You can execute this code in useEffect which will won't be executed until after the component is mounted.
For example for client side fetch:
useEffect(() => {
const doMyAxiosCall = async () => {
...
// Set localStorage values here
}
// This will only be executed on the client
doMyAxiosCall();
}, [])

How to mock a third party React component using Jest?

TLDR; what's the proper way to mock a React component imported from a third-party library?
I'm testing a component called <App/>. It consumes a 3rd part component called <Localize/> provided by a library called localize-toolkit.
I'm having some trouble mocking <Localize/> using Jest.
Here is how I've tried mocking it.
jest.mock('localize-toolkit', () => ({
// Normally you pass in a key that represents the translated caption.
// For the sake of testing, I just want to return the key.
Localize: () => (key:string) => (<span>{key}</span>)
}));
And I've written a unit test for <App/> that looks like this.
it('Test', () => {
const component = render(<App/>);
expect(component).toMatchSnapshot();
}
)
It will pass, however this is the warning message returned.
Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render.
And when I look at the snapshot, I get a series of periods "..." where the localized caption should appear.
Am I not mocking the Localize component properly?
Here's how I ended up doing it.
Note how the third-party component Localize needs to be returned as a function.
jest.mock('localize-toolkit', () => ({
Localize: ({t}) => (<>{t}</>)
}));
and in case there are multiple components, and you only want to mock one of them, you can do this:
jest.mock("localize-toolkit", () => {
const lib = jest.requireActual("localize-toolkit");
return {
...lib,
Localize: ({t}) => (<>{t}</>),
};
});
We can mock the 3rd party library for example in my case i need to mock react-lazyload
Component.tsx
import LazyLoad from 'react-lazyload';
render() {
<LazyLoad><img/></LazyLoad>
}
In jest.config.js
module.exports = {
moduleNameMapper: {
'react-lazyload': '/jest/__mocks__/react-lazyload.js',
}
}
In jest/mocks/react-lazyload.js
import * as React from 'react';
jest.genMockFromModule('react-lazyload');
const LazyLoad = ({children}) => <>{children}</>;
module.exports = { default: LazyLoad };

Where could we put `Office.onReady()` to make sure Office.js could fully load every time

I'm debugging an Excel add-in built in React. Previously, the developer coded the app as if it was a website rather than an Excel add-in, and did not pay attention to Office.js. As a result, when we run it in Excel, we have the famous error Error: Office.js has not fully loaded. Your app must call "Office.onReady()" as part of it's loading sequence (or set the "Office.initialize" function). If your app has this functionality, try reloading this page.
I searched for Office.onReady and Office.initialize in the whole project, I did not find them.
Here is frontend/src/index.tsx, where dva is a "framework based on redux, redux-saga and react-router (Inspired by elm and choo)".
import 'react-app-polyfill/ie11';
import 'react-app-polyfill/stable';
import dva from 'dva';
import './index.css';
import router from './router';
import AuthModel from './models/auth';
import SubscribtionModel from './models/subscription';
import AppModel from './models/app';
import { initializeIcons } from 'office-ui-fabric-react/lib/Icons';
//#ts-ignore
//import createLoading from 'dva-loading';
// 1. Initialize
const app = dva();
//app.use(createLoading());
// 2. Plugins
// app.use({});
// 3. Model
//#ts-ignore
app.model(AuthModel);
app.model(SubscribtionModel)
app.model(AppModel);
// 4. Router
//#ts-ignore
app.router(router);
// 5. Start
app.start('#root');
initializeIcons();
Does anyone know where I should put Office.onReady() such that we could make sure Office.js can fully load every time the add-in is launched?
Edit 1:
I tried to mimic this file and changed one of my index.tsx to
render() {
const { items, farItems } = this.props;
console.log("Here is Office:");
console.log(Office);
return (
<AwaitPromiseThenRender
promise={Promise.resolve().then(async () => {
await Office.onReady();
})}
>
<CommonHeader
items={items.map((item: IHeaderItem) => this.renderItem(item))}
farItems={farItems.map((item: IHeaderItem) => this.renderItem(item))}
/>
</AwaitPromiseThenRender>
);
}
It returned a TypeScript error: Property 'onReady' does not exist on type 'typeof Office'. TS2339, whereas the object Office is well printed in the console. Does anyone have any idea of the problem?
Edit 2:
I tried to add the following code in my frontend/src/index.tsx above. It still returned Property 'onReady' does not exist on type 'typeof Office'. TS2339.
export function render(oldRender: () => void) {
Office.onReady((reason: any) => {
oldRender();
});
}
You can put it in componentWillMount
async componentWillMount() {
await hostApp.onReady();}
If you use class component,you can put it in componentWillMount.
If you use function component, you can put it in Effect hooks like this:
useEffect(()=>{
Office.onReady(function(info) {
console.log(info);
if (info.host === Office.HostType.Word) {
console.log("hhhhhhhhhhh")
}
if (info.platform === Office.PlatformType.PC) {
// Make minor layout changes in the task pane.
console.log("ooooooooooooo")
}
console.log(`Office.js is now ready in ${info.host} on ${info.platform}`);
});
})

Nextjs check if initial render was done on server side

I have an image lazy load component that just renders the image src when on the server, and renders a loader when on client pending when the actual image is lazyloaded.
The problem.
After the initial render and client side takes over, the image src mismatch. This is because the server set the actual src, but the client is setting the loader (placeholder).
Question.
Is there a way to detect that this initial render was server rendered? just before the client renders/mounts
You can find out if it is currently executed on the server by checking for req attribute inside getInitialProps
Example page code
function Page({ isServer }) {
return <div>Is this page on the server? - {isServer ? 'YES' : 'NO'}</div>;
}
Page.getInitialProps = async ({ req }) => {
return { isServer: !!req };
};
export default Page;
Some info on official repo about isServercheck
You can check that typeof Window === 'undefined'
The fix is to use useEffect as described in the Next.js docs at https://nextjs.org/docs/messages/react-hydration-error
do this:
import { useState, useEffect } from 'react'
function AnyComponent() {
const [isSSR, setIsSsr] = useState(true)
console.log(`isSSR: `, isSSR);
useEffect(() => setIsSsr(false))
return (
<span>Is SSR? {isSSR ? 'Yes!' : 'No!'}</span>
)
}
Obviously, this trivial example will update so quickly that you would only ever see "Is SSR? No!" but the first render will be yes. Check the console.
Why? useEffect is only executed in the browser.

Resources