How to fix this warning "useLayoutEffect" related warning? - material-ui

I am using NextJS with Material UI and Apollo. Although, everything is working properly but the warning is not going. It seems to me that a lot of Material UI components are using useLayoutEffect which is warned by React. The error is below.
Warning: useLayoutEffect does nothing on the server, because its effect cannot be encoded into the server renderer's output format. This will lead to a mismatch between the initial, non-hydrated UI and the intended UI. To avoid this, useLayoutEffect should only be used in components that render exclusively on the client. See fb.me/react-uselayouteffect-ssr for common fixes.

The problem is solved. I was suspecting it occurred for Material UI but it is actually happening for Apollo. I am posting the solution here to let others know.
in Apollo configuration file I needed to say the application is using Server Side Rendering. Please check the code below. If you are not using TypeScript then just remove the Types. In the last line { getDataFromTree: 'ssr' } object solved the issue. I hope it will help you.
import { InMemoryCache } from 'apollo-cache-inmemory';
import ApolloClient from 'apollo-client';
import { createHttpLink } from 'apollo-link-http';
import withApollo from 'next-with-apollo';
type Props = {
ctx?: {};
headers?: {};
initialState?: {};
};
const createClient = ({ ctx, headers, initialState }: Props) =>
new ApolloClient({
cache: new InMemoryCache().restore(initialState || {}),
link: createHttpLink({ uri: process.env.API_ENDPOINT })
});
export default withApollo(createClient, { getDataFromTree: 'ssr' });

I had the same problem using jest, enzyme and Material UI, but I was not using Apollo. If you encounter this problem using Material UI, a simple work around is to add to your test config file (src/setupTests.js) the following:
import React from 'react';
React.useLayoutEffect = React.useEffect;
Sources: here and here and here.
Otherwise, if your stack includes Apollo, you could try

Related

Stop huge error output from testing-library

I love testing-library, have used it a lot in a React project, and I'm trying to use it in an Angular project now - but I've always struggled with the enormous error output, including the HTML text of the render. Not only is this not usually helpful (I couldn't find an element, here's the HTML where it isn't); but it gets truncated, often before the interesting line if you're running in debug mode.
I simply added it as a library alongside the standard Angular Karma+Jasmine setup.
I'm sure you could say the components I'm testing are too large if the HTML output causes my console window to spool for ages, but I have a lot of integration tests in Protractor, and they are SO SLOW :(.
I would say the best solution would be to use the configure method and pass a custom function for getElementError which does what you want.
You can read about configuration here: https://testing-library.com/docs/dom-testing-library/api-configuration
An example of this might look like:
configure({
getElementError: (message: string, container) => {
const error = new Error(message);
error.name = 'TestingLibraryElementError';
error.stack = null;
return error;
},
});
You can then put this in any single test file or use Jest's setupFiles or setupFilesAfterEnv config options to have it run globally.
I am assuming you running jest with rtl in your project.
I personally wouldn't turn it off as it's there to help us, but everyone has a way so if you have your reasons, then fair enough.
1. If you want to disable errors for a specific test, you can mock the console.error.
it('disable error example', () => {
const errorObject = console.error; //store the state of the object
console.error = jest.fn(); // mock the object
// code
//assertion (expect)
console.error = errorObject; // assign it back so you can use it in the next test
});
2. If you want to silence it for all the test, you could use the jest --silent CLI option. Check the docs
The above might even disable the DOM printing that is done by rtl, I am not sure as I haven't tried this, but if you look at the docs I linked, it says
"Prevent tests from printing messages through the console."
Now you almost certainly have everything disabled except the DOM recommendations if the above doesn't work. On that case you might look into react-testing-library's source code and find out what is used for those print statements. Is it a console.log? is it a console.warn? When you got that, just mock it out like option 1 above.
UPDATE
After some digging, I found out that all testing-library DOM printing is built on prettyDOM();
While prettyDOM() can't be disabled you can limit the number of lines to 0, and that would just give you the error message and three dots ... below the message.
Here is an example printout, I messed around with:
TestingLibraryElementError: Unable to find an element with the text: Hello ther. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.
...
All you need to do is to pass in an environment variable before executing your test suite, so for example with an npm script it would look like:
DEBUG_PRINT_LIMIT=0 npm run test
Here is the doc
UPDATE 2:
As per the OP's FR on github this can also be achieved without injecting in a global variable to limit the PrettyDOM line output (in case if it's used elsewhere). The getElementError config option need to be changed:
dom-testing-library/src/config.js
// called when getBy* queries fail. (message, container) => Error
getElementError(message, container) {
const error = new Error(
[message, prettyDOM(container)].filter(Boolean).join('\n\n'),
)
error.name = 'TestingLibraryElementError'
return error
},
The callstack can also be removed
You can change how the message is built by setting the DOM testing library message building function with config. In my Angular project I added this to test.js:
configure({
getElementError: (message: string, container) => {
const error = new Error(message);
error.name = 'TestingLibraryElementError';
error.stack = null;
return error;
},
});
This was answered here: https://github.com/testing-library/dom-testing-library/issues/773 by https://github.com/wyze.

Inconsistent classNames between server and client rendered components

I have an issue whereby my client rendered components class names (custom class names created with makeStyles) are of the format jss1234, however when rendering on the server they are of the format makeStyles-name-1234. Causing issues when I then hydrate.
I have followed the server side set up here: https://material-ui.com/guides/server-rendering/#handling-the-request pretty much to the letter.
My client entry point looks something like:
const Main = () => {
useEffect(() => {
// clean up any server side generated styles
const jssStyles = document.querySelector('#jss-server-side');
if (jssStyles) {
jssStyles.parentNode.removeChild(jssStyles);
}
}, []);
return (
<ThemeProvider theme={ theme }>
<BrowserRouter>
<App />
</BrowserRouter>
</ThemeProvider>
);
};
ReactDOM.hydrate(<Main />, root);
This is only an issue in production and I have ensured that both my server and client side code have process.env.NODE_ENV === 'production'.
I don't mind which format my classnames are, as long as they are consistent. I have tried using the StylesProvider and creating a new generateClassName function to force one way or the other, but it just doesn't seem to work. Client is always jss and server is always makeStyles prefixed.
Is there any other method of configuring this that I am missing?
thanks, in advance, I will update the question with any more information as I find it.
Update
On closer inspection it looks as though I cannot override the generateClassName function, I pass one in and a function is generated, but its not that one that gets called.
I have the following:
const generateClassName = createGenerateClassName({ disableGlobal: true });
const css = new ServerStyleSheets({ generateClassName });
const markup = ReactDOMServer.renderToString(
css.collect(
<StylesProvider generateClassName={ generateClassName }>
<ThemeProvider theme={ theme }>
...
and in my client:
const generateClassName = createGenerateClassName({ disableGlobal: true });
return (
<StylesProvider generateClassName={ generateClassName }>
<ThemeProvider theme={ theme }>
...
But the disableGlobal never takes effect, it looks as though it never actually uses this function. I must be missing some configuration, however I find the documentation about this stuff a little fragmented and it seems to suggest I wouldn't need to use StylesProvider on the server with the new API.
thanks in advance.
I had the some problem. I follow the exact tutorial in https://material-ui.com/guides/server-rendering/#handling-the-request. The development build works fine, but when running the production build on node, the server side rendering used development class names, causing a class mismatch on hydration.
I solved this by including the NODE_ENV enviroment variable on node bootstrap on production server.
NODE_ENV=production node server.js
So as it turns out this was indeed an issue with NODE_ENV.
In my case I was compiling my client code with webpack, but not the external node modules (using webpack nodeExternals). The command to do this was something like:
NODE_ENV=production webpack ... && node server.js
changing these to seperate npm tasks or something like:
NODE_ENV=production webpack ... && NODE_ENV=production node server.js
This solved the issue and I have been able to remove the StylesProvider from my code, as it is not needed with the new MUI v4 API.
Thanks and keep up the good work.

How to specify event handlers when using lit-html?

The main documentation under [Writing Templates] the following example for binding an event handler with lit-html is provided.
html`<button #click=${(e) => console.log('clicked')}>Click Me</button>`
Adding this a simple page with the default render and html functions imported and calling render however doesn't seem to render the button. If you remove the #click event binding then the button is rendered. There must be something I'm missing or a serious bug in the library.
version: 0.10.2
The links below relate to how events handler bindings work in lit-html:
https://polymer.github.io/lit-html/guide/writing-templates.html
https://github.com/Polymer/lit-html/issues/399
https://github.com/Polymer/lit-html/issues/145
https://github.com/Polymer/lit-html/issues/273
https://github.com/Polymer/lit-html/issues/146
The previous accepted answer was wrong. lit-extended is deprecated and that workaround only worked for a period in 2018 while lit-html was switching over to the new syntax.
The correct way to consume an event is:
html`<button #click=${e => console.log('clicked')}>Click Me</button>`
You can configure the event by assigning an object with a handleEvent method too:
const clickHandler = {
// This fires when the handler is called
handleEvent(e) { console.log('clicked'); }
// You can specify event options same as addEventListener
capture: false;
passive: true;
}
html`<button #click=${clickHandler}>Click Me</button>`
There is also lit-element which gives you a base for building web components with Lit and TypeScript enhancements to move the boilerplate noise of creating event handlers into decorators:
#eventOptions({ capture: false, passive: true })
handleClick(e: Event) { console.log('clicked'); }
render() {
return html`<button #click=${this.handleClick}>Click Me</button>`
}
It appears that in order to use event handler bindings one must not use the standard lit-html API but instead lit-extended which appears to be distributed along with lit-html. Changing import statement to import lit-extended and changing the attribute syntax as shown below seems to work for me.
Before:
import { html, render } from "lit-html";
html`<button #click=${(e) => console.log('clicked')}>Click Me</button>`
After (working):
import { html, render } from "lit-html/lib/lit-extended";
html`<button on-click=${(e) => console.log('clicked')}>Click Me</button>`
Note that the #click syntax didn't seem to work for me at all regardless of what several examples show in the GitHub issues as well as the main documentation. I'm not sure if the above syntax is the preferred way or only way to do event binding but it seems to be one that is at least working.
To me it looks like this may be a good candidate for contributing improvements to the lit-html documentation.

I couldn't import/require react-component class inside a node_modules package on web development

my code is like:
import Render from './AppeRender';
import { Component } from 'react';
export default class appDB extends Component {
render () {
return Render.call(this, this.props, this.state);
}
}
and what i'm getting is:
Module parse failed: /home/projects/node_modules/DB/Db.js Line 5: Unexpected token
You may need an appropriate loader to handle this file type.
Note: Error only comes in web setup, it's working fine in android and in IOS i haven't tried yet.
Does anyone have any idea regarding this.
I think what is wrong here is that you using import twice (and the second one is a destructure.
Try this instead:
import { Component } from 'react';
import Render from './AppeRender';
You can bind Component in one import.
The second import could have been changed to a straight destructure:
const { Component } = React;
But, there is no reason to do this if you are only using Component.
Using import on an Object is not really correct (however, it might work with some implementations), I assume that is why the error was occurring.

Why is ic-ajax not defined within certain functions in Ember CLI?

Forgive my ignorance, but I can't get ic-ajax working inside of certain
functions.
Specifically, I'd like to get a test like this working, but for Ember CLI:
e.g. http://coderberry.herokuapp.com/testing-your-ember-application#30
I can call ajax inside Ember.Object.Extend and outside of functions and object definitions, but not in modules, tests, or Ember.Route's model function.
Am I misunderstanding something or is there a misconfiguration in my app?
I've figured out that within functions I can do:
ajax = require('ic-ajax')['default'];
defineFixture = require('ic-ajax')['defineFixture'];
but I'm pretty sure import at the top of the file is supposed to work.
I'm experiencing this on Ember 0.40.0 (both in my existing app and a fresh app). See below for more specifics where I'm finding it undefined. Setting var ajax = icAjaxRaw outside of the functions does not work either. I'm at a bit of a loose end so any help you could give in this regard would be great.
users-test.js:
import ajax from 'ic-ajax';
import { raw as icAjaxRaw } from 'ic-ajax';
import { defineFixture as icAjaxDefineFixture } from 'ic-ajax';
debugger;
---> icAjaxDefineFixture IS defined here
module('Users', {
setup: function() {
App = startApp();
debugger;
icAjaxDefineFixture --> UNDEFINED
},
teardown: function() {
Ember.run(App, App.destroy);
}
});
test("Sign in", function() {
icAjaxDefineFixture --> UNDEFINED
expect(1);
visit('/users/sign-in').then(function() {
equal(find('form').length, 1, "Sign in page contains a form");
});
});
Brocfile.js (I don't think these are actually needed with the new ember-cli-ic-ajax addon):
app.import('vendor/ic-ajax/dist/named-amd/main.js', {
exports: {
'ic-ajax': [
'default',
'defineFixture',
'lookupFixture',
'raw',
'request',
]
}
});
Had the same problem. Turns out it is a Chrome debugger optimization issue, checkout this blog post http://johnkpaul.com/blog/2013/04/03/javascript-debugger-surprises/
While debugging, if you try to use a variable from a closure scope in the console, that wasn’t actually used in the source, you’ll be surprised by ReferenceErrors. This is because JavaScript debuggers optimize the hell out of your code and will remove variables from the Lexical Environment of a function if they are unused.
To play around in debugger, I've just typed ajax; inside of the closure and variable magically appeared.