Prevent Vue.js from rendering component - javascript

My case is that I have a static component on the desktop and it must become carousel on mobile.
The component is rendered server side because of seo and I use is="my-component" to trigger vue on it. Typically when I duplicate the markup and check in created() the breakpoint, I can trigger some carousel constructor. However, if a breakpoint is set to desktop, vue will still rerender component which is redundant.
I know that one case may not be that effective, but I have a lot of performance and parsing problems because of vue in my previous project, so I need to keep performance in mind from the beginning.
Is it possible to somehow prevent rendering on beforeCreate() hook, but still be able to use it in some conditional?

As I have read your comment, and you would like to use something else that is not v-if, I can think only in two ways of doing it.
1) If you are using vue-router you can run make use of Lazy Loading Routes which is basically a function that can return an import('component') (which is a promise).
MobileCarousel.ts
import { isMobile } from '#/utils/mediaQuery';
const MobileCarousel = (): Promise<Vue> | void => {
if (!isMobile()) {
return;
}
return import('#/components/MobileCarousel/MobileCarousel.vue');
};
export default MobileCarousel;
Routes.ts
import MobileCarousel from '#/components/MobileCarousel/MobileCarousel.ts';
...
{
path: 'route-that-has-a-mobile-only-carousel',
name: 'mobile-only-carousel',
component: MobileCarousel,
},
enter code here
My only concern with this approach is related to the server-side rendering. As I never have played with server side-rendering with Vue I cannot assure you that it will work as you expect, you can give a try. Hope it helps you.
2) Apart from using Lazy Loading Routes, I believe that a Vue component with a render function that returns only if it is mobile also can be useful for your case.

Related

Is it a good idea to use React.Context to inject UI-Components?

I plan to build a react component library. The react components are UI-Components but should only implement a specific logic. I want the user to be able to define a set of atoms (basic react components) that are used to compose the actual components. My main goal is to make the library independent of a specific UI-Component-Library like MaterialUI, ChakraUI, etc.
My idea was to use a React.Context to inject the components like this:
// Button Atom
const Button: FC = ({ children }) => (<button>{children}</button>)
const atoms = { button: Button }
const AtomContext = createContext(atoms);
// "higher" component
const HigherComponent: FC = () => {
const atoms = useContext(AtomContext)
// Logic ...
return (
<atoms.button>click me</atoms.button>
)
}
export default function App() {
return (
<AtomContext.Provider value={atoms}>
<HigherComponent />
</AtomContext.Provider>
);
}
This solves my problem. But I'm not sure if it is a good idea. Are there better ways to inject UI-dependencies? What may be problems with my approach?
This is a question that can result in multiple answers based on personal experience.
But overall the idea to pass component trough context is a bit of misuse of that concept.
Also it does not bring much benefit from making a standalone library that can be imported via package.json or making a folder with components and importing them.
If you need to pass some specific things to your components you can have a custom provider as you did there, but as far as the component themselves, there is no common sense to use context.
If you end goal is to shorten the list of imports of component with custom context hook and just getting them like that, I think that is a bad tradeoff overall.
I just read this article about using react context for dependency injection. The authors opinion is, that react context for injecting non-react dependencies into components is good practice. However, I'm not sure if that applies to injecting a specific set of react components. Since, react components are nothing more than functions I think it should be alright to use reacts context for that.

When is it necessary to use `rerender` with the React Testing Library?

In times past, my colleagues and I would typically write React Testing Library (RTL) tests for the main parent components, which often have many nested child components. That testing made sense and worked well. Btw the child components in question are very much dedicated to that parent component and not of the reusable variety.
But now we're trying to write RTL tests for every single component. Today I was trying to build tests for an Alerts component, which is the parent of an Alert component and about 4 levels down from the top-level component. Here's some sample code in my test file:
function renderDom(component, store) {
return {
...render(<Provider store={store}>{component}</Provider>),
store,
};
}
let store = configureStore(_initialState);
const spy = jest.spyOn(store, 'dispatch');
const { queryByTestId, queryByText, debug } = renderDom(
<Alerts question={store.getState().pageBuilder.userForm.steps[0].tasks[0].questions[1]} />,
store
);
I then started writing the typical RTL code to get the Alerts component to do its thing. One of these was to click on a button which would trigger an ADD_ALERT action. I stepped through all of the code and the Redux reducer was apparently working correctly with a new alert, as I intended, yet back in the Alerts component, question.alerts remained null whereas in the production code it was definitely being updated properly with a new alert.
I spoke with a colleague and he said that for this type of test, I would need to artificially rerender the component like this:
rerender(<Provider store={store}><Alerts question={store.getState().pageBuilder.userForm.steps[0].tasks[0].questions[1]} /></Provider>);
I tried this and it appears to be a solution. I don't fully understand why I have to do this and thought I'd reach out to the community to see if there was a way I could avoid using rerender.
It's hard to be certain without seeing more of your code, but my typical approach with RTL is to take the fireEvent call that simulates clicking the button and wrap it in an act call. This should cause React to finish processing any events from your event, update states, rerender, etc.
Alternatively, if you know that a particular DOM change should occur as a result of firing the event, you can use waitFor. An example from the React Testing Library intro:
render(<Fetch url="/greeting" />)
fireEvent.click(screen.getByText('Load Greeting'))
await waitFor(() => screen.getByRole('alert'))

Is it good practice to set CSS variable in the React's render method?

I have created a post yestarday and deleted it by error
I have to create a component with specific style. The problem is that the componentDidMount() method creates some king of flickering when rendering the component. It is the time that the CSS became set.
I think my component is ressource's demanding so the componentDidMount() occurs with a little frame or two of lagging. That why I am providing here just a snippet instead of a full demo, my little demo works well with zero specific flickering.
I have then entering the value in the render's method. Like the following:
componentDidMount(){
// the CSS rendering will lag by releasing a flickering :/
}
render(){
// awesome, by setting my style in the render the good style is returned instantly.
if (Math.round(window.scrollY + window.innerHeight +50) >= Math.round(document.body.scrollHeight)) {
document.documentElement.style.setProperty('--background-transition', "")
document.documentElement.style.setProperty('--is-page-bottom', "orange");
document.documentElement.style.setProperty('--background-transition', "all 0.3s ease-out")
}
else{
//setIsPageBottom_CSSVariable("rgba(84, 80, 79,0.85)")
document.documentElement.style.setProperty('--background-transition', "")
document.documentElement.style.setProperty('--is-page-bottom', "purple");
document.documentElement.style.setProperty('--background-transition', "all 0.3s ease-out")
}
Someone yesterday told me that I should use react React Hook to manage this case, or componentDidMount but componentDidMount fails in my case because of the flickering before installing the relevant background color.
I am new to React Hook so if I understand well I can make something like document.documentElement.style.setProperty in useEffect directly and it would be coherent with the design of React hook?
Meanwhile I find my solution is just good because when reading the official React's documentation, it is noted that:
The render() function should be pure, meaning that it does not modify
component state, it returns the same result each time it’s invoked,
and it does not directly interact with the browser.
If you need to interact with the browser, perform your work in
componentDidMount() or the other lifecycle methods instead. Keeping
render() pure makes components easier to think about.
Okay, if I can think about my component with a limpid manner so it is in no way a bad thing to do it isn't it? Just not the best practice I assume.
I have been advised to use an addeventlistener but here it is about instantly rendering my style on the first render component's loop. So addeventlistener is a solution for a different case that the one I am on.
In all case, which alternative would you recommend that avoid flickering when inserting my CSS setting in componentDidMount()? What is the link with the React hook?
This is how you can use useEffect
//componentDidMount replacement
useEffect(() => {
//your code goes here
}, [])
You might also consider useLayoutEffect as it fires synchronously with the DOM changes. If normal useEffect retains the issue, then you should be able to use this instead.
No, it's not good practice - you would be better off changing the class name dynamically using react, dependent on the internal state of the component.
So you could use the useState hook, and have your component in a 'loading state' which would then apply a class.
Then you could add / remove class names and associated styles using react as well, rather than inserting them with JS.
Have a look at the classNames package: https://www.npmjs.com/package/classnames
Your css would then be: .additional-class {
background: red;
}
or whatever you wanted.
Applying styles directly from React is somewhat missing the point of using react, to be honest. If you do want to do that, there are ways but should be limited, really: How to modify the DOM directly in ReactJS
https://reactjs.org/docs/refs-and-the-dom.html

VUE Deallocating memory using THREE JS between routes

I'm in the process of learning VUE JS. I have a very basic SPA that routes between various pages.
I have a number of THREE JS demos that I've built in my spare time and I've noticed that if I jump between pages on them eventually they grind to a halt from a build up of memory. I want to avoid pasting their scripts in here as they're massive and I don't think the problem lies within them.
I think the problem the lies somewhere in between how I'm instantiating them and my lack of knowledge in VUE JS is what's causing me trouble with this problem.
Here is an example of one of the views I'm routing to in VUE JS:
<template>
<div class="particles">
<main-menu></main-menu>
<div id="demo"></div>
</div>
</template>
<script>
import mainMenu from 'root/views/menu.vue';
export default {
components: { mainMenu },
mounted() {
var Particles = require('root/webgl/particles.js');
var demo = new Particles();
demo.run();
}
}
</script>
The original demos were built using traditional JavaScript (it's a ES5/6 class) and I was hoping that I could just plug them into my VUE SPA. Within each demo I am doing things like:
this.vsParticles = document.getElementById('vs-particles').textContent;
to load the shaders and to attach my THREE demo to a DOM element.
The issue I'm having is that somewhere something isn't being deleted. Within my demos i'm not creating anything new in the DOM and they run fine in non-SPA demos, but once I place them into a SPA app and jump between pages they build up.
I'm under the impression that when you change routes everything should get wiped. So all of the elements within my template tags and any objects I create within mounted(). But this doesn't seem to be the case and I'm just wondering is there something extra I need to include in VUE to completely clean everything up inbetween pages?
As Mathieu D. mentionned, you should move your require outside of the method.
Also, you may need to clear the WebGL context on the destroyed () Vue Component's hook.
I am not sure if it is the right way to do, but here is how I dispose it in my program :
this.renderer.forceContextLoss()
this.renderer.context = null
this.renderer.domElement = null
this.renderer = null
Could you try to import particles.js out of mounted method ?
For example, under your import of mainMenu ?
The import would be done one for all of your instanciation of Particles.
This will give you this code :
import mainMenu from 'root/views/menu.vue';
import Particles from 'root/webgl/particles.js';
export default {
components: { mainMenu },
mounted() {
var demo = new Particles();
demo.run();
}
}
You could also read reactivity in depth in the documentation. It will help you understand how VueJS store and access data. I don't know if in your component code you store some data of your ThreeJS examples, but if so, it could consume some memory. In that case, use destroyed hook to clean your data.

Utilizing componentDidMount when switching between different instances of the same React component

According to the react documentation: http://facebook.github.io/react/docs/component-specs.html#mounting-componentdidmount we are adviced use componentDidMount for AJAX call that should be issued when a component is brought into view.
However, when switching between to instances of the same component with different props, componentDidMount is only called for the first component. So what are we supposed to do in this situation?
Currently I have the following workaround: In componentDidMount i do my AJAX call and In componentDidUpdate I compare old and new props to check if I am on a new "instance", and if so I do my AJAX call. But that seems exactly like a workaround. So my question is: is this really the way to do it?
I am aware that I could wrap my component in different empty components to solve my problem. However, this is not possible because we are building a data driven application that uses configurable components and it makes sense to use the same component with different configurations - which is where I'm running into problems.
I am aware that we are actually talking about react elements and not instances as such - witch I guess is part of the problem. Probably I have different react elements utilizing the same instance.
I have made a tiny example to illustrate the react behavior, using plain react (just to make sure I wasn't tricked by react-router or redux and what else we are using the real app):
class Foo extends React.Component {
componentDidMount() {
console.log('componentDidMount ' + this.props.foo);
}
componentDidUpdate() {
console.log('componentDidUpdate ' + this.props.foo);
}
render() {
return <div>Route is {this.props.foo}</div>;
}
}
function navigated() {
ReactDOM.render(
<Foo foo={window.location.hash} />,
document.getElementById('app')
);
}
window.addEventListener('hashchange', navigated, false);
navigated();
Initially when I go to #/bar I get 'componentDidMount #/bar' and when I go to #/baz i get 'componentDidUpdate #/baz'.
I seems like this unanswered question is a specific case of the same issue: React does not know when i render the same component
You can add the key property with unique value for each of hashes:
ReactDOM.render(
<Component hash={hash} key={hash} />, domNode
);
This will update the component every time when the hash is really changed.
https://facebook.github.io/react/docs/multiple-components.html#dynamic-children
TL DR - your 'workaround' looks correct to me
When you initially render the component componentDidMount is called, when you change the hash prop componentDidUpdate is called. It is still the same component, it is just that a specific prop has changed value. In your case, you have logic (running an AJAX call when hash changes) that is specific to your application. React does not known that the hash prop is special, you make is special by adding the logic in componentDidMount. So I believe you have a good interpretation of the React docs and this way of achieving your goal is perfectly valid.

Categories

Resources