The docs say React-admin encourages e2e instead of unit tests, and looking at the spec files I'm not seeing the usual stubbing and mocking to isolate the code-under-test. Further, when I try to test my own <List /> with a data grid, based on the List.spec.js, I get the error below. Is this an example of something that should only get covered by e2es, or can I isolate it, and if so how?
BTW: Wrapping it in an act() still produces the same error.
When testing, code that causes React state updates
should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
This ensures that you're testing the behavior the user
would see in the browser.
Learn more at fb.me/react-wrap-tests-with-act
in List (at mm-services.tsx:7)
in ServiceList (at mm-services.test.tsx:54)
in ThemeProvider (at mm-services.test.tsx:53)
in Router (created by TestContext)
in Provider (created by TestContext)
in TestContext
Related
I have a Blazor WebAssembly site, and the component tree has grown quite a bit.
In my Home component, a change in a child component "Foo" can happen that causes parameter property "IsChecked" to change; one of these properties is passed into a different child component "Bar". So therefore, in my Home component, Foo's EventCallback IsCheckedChanged invokes StateHasChanged() so that the Home component and therefore all its children re-render.
This can be slow...for me, it is taking 1-2 seconds to render and paint when even only one boolean property changes. Supposing the user is checking and unchecking several checkboxes to trigger this rerender: this delay would be quite unwelcome on each one. (I have already ruled out that any of my other code in Home could be slowing things down.) So I gather that I have to figure out which parts of which components are slowest and perhaps have ShouldRender reutrn false in some cases, or otherwise do some optimization therein. My trouble is that I don't see which components are most contributing to the render time.
So is there any way to see or write out the time it takes a Blazor component to render, as "render" is described in the lifecycle document? I assume any such render time would include the render time of all children, of course.
Is there any better way to profile Blazor WebAssembly render times?
(1) Rendering from C# code to HTML's DOM is low-level process. Blazor debugging process only allow see value of objects/values. No out-of-the-box for tracing Blazor redering process (generate DOM from C# code). You can see trick
(2) You can use DOM change tracing:
https://dev.to/apvarun/chrome-devtools-detecting-element-changes-gh6
https://developers.google.com/web/updates/2012/02/Detect-DOM-changes-with-Mutation-Observers
https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
(3) A trick use counter put inside per component by using a Blazor BaseComponent. https://www.codeproject.com/Articles/5290129/Exploring-Blazor-Component-Rendering
https://allinoneserver.azurewebsites.net/wasm.html
use BaseCounter
#inherits BaseCounter
// Markup the same as BaseCounter
#code {
protected override string buttoncolor => "btn-success";
protected override Task OnInitializedAsync()
{
Service.CounterChanged += ReRender;
return base.OnInitializedAsync();
}
protected void ReRender(object sender, EventArgs e) =>
this.InvokeAsync(this.StateHasChanged);
}
and components extend from the BaseCounter, when per component called, counter will increase value, it help you see what is rendering and order of rendering process.
(4) Outside the question, for improving performance, see Optimize rendering speed at https://learn.microsoft.com/en-us/aspnet/core/blazor/webassembly-performance-best-practices?view=aspnetcore-5.0#optimize-rendering-speed
Is it possible to run some custom script for every page?
E.g., I want to run alert(1); on every page. How can I do that without the sizzling of any components?
I know it can be done by creating jsx component and using it in every .mdx file (but every doc then should be a .mdx file). So it's not the thing I'm looking for.
Docusaurus 2 user here! 👋
Docusaurus is Server-Side Rendered and then hydrated to function as a Single Page Application. Without knowing more about what you want to achieve, I can only try to give you a general advice.
One way of achieving this is to create your own plugin, it gives you access to the execution context, such as router events.
I currently use this for analytics reporting when the user changes page. It's not yet documented, but there's a good example in the Docusaurus 2 repository in the docusaurus-plugin-google-analytics package.
Here's a fragment of what I use, this only executes when a new page is loaded, which fits my use case perfectly. There may be another lifecycle hook called when the page is hydrated that I haven't found yet.
analytics-module.js
import ExecutionEnvironment from "#docusaurus/ExecutionEnvironment";
export default (function () {
if (!ExecutionEnvironment.canUseDOM) {
return null;
}
return {
onRouteUpdate({ location }) {
_paq.push(["setCustomUrl", location.pathname]);
_paq.push(["setDocumentTitle", document.title]);
_paq.push(["trackPageView"]);
},
};
})();
I have a Blazor Server app that is a multi-step "wizard" form. After each relevant step the state is adjusted, and new HTML is shown/hidden via conditional statements (simple example below).
if (IsStepSignature)
{
<div>Signature HTML here</div>
}
This all works just fine. My problem comes when I need to invoke some JS logic on the dynamically generated HTML from above (e.g. click handlers to hook up external JS libraries). When I handle the "Next" click, I can invoke the JS just fine...but it is not yet seeing the dynamic HTML from above. Is there a way to invoke some JS, and control it so that it doesn't execute until after the page is redrawn from the C# code execution?
5/18/2020 Update from Nik P
Can leverage some flags and use OnAfterRenderAsync to control this ordering. This does work, but it does require some extra hops to/from the server. Below is what I see when implementing this. This may just be the nature of Blazor Server, as one of the pros/cons is some known added chattiness. In total these requests were 2.5K, so extremely small.
CLIENT --> Browser Dispatch Event (NEXT CLICK)
Render <-- SERVER
CLIENT --> Render Complete
Invoke JS <-- SERVER
CLIENT --> Invoke Complete
The issue you are having has to do with the element not existing in the client side HTML at all until after the re-render takes place. So one way to do this is to set a boolean flag in your C# code that says there is code that needs to be run after the render, and populate support fields that you will need for your JS Interop. Whenever you need to run the JS interop, set your flag to true, set your support fields to the values you need for the JS interop call, and then do something that kicks a DOM diff calculation. (Even StateHasChanged should be enough, but adding your items conditionally as you mentioned will also do it) Then, override your OnAfterRenderAsync method as follows:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if(firstRender)
{
// any first render code
}
if(yourFlag)
{
YourJSInteropMethod(supportField1, supportfield2);
yourflag = false;
}
}
The simplicity in this approach is that the DOM update will always happen ahead of the OnAfterRenderAsync call, so your HTML will be populated with what you are targeting with JS.
I'm trying to show an activity indicator, when I go from one page to another. The target page contains many components within it, and it takes time to load. that's why I need some way to listen when all the child components are loaded, and at that moment tell my variable isBussy to be false
<template>
<StackLayout>
<ActivityIndicator :busy="isBussy" v-if="isBussy" />
<StackLayout v-else>
<Component1 />
<Component2 />
<Component3 />
<Component4 />
</StackLayout>
<StackLayout>
</template>
<script>
import Component1 from '~/components/Component1'
import Component2 from '~/components/Component2'
import Component3 from '~/components/Component3'
import Component4 from '~/components/Component4'
export default {
data() {
return {
isBussy: true
}
},
mounted() {
this.$nextTick(function() {
// Code that will run only after the
// entire view has been re-rendered
this.isBussy = false
})
}
}
</script>
this code does not work, since once the navigation is indicated from the previous page with:
#tap="$goto('otherPage', { props: { foo: bar } })"
it remains stuck on the initial page, and all the components begin to load in the background of the destination page, but without displaying the parent page, changing to this, only when the whole process ends, and never show/hide the activity indicator as expected.
By the way this expected behavior works perfectly when i do request and process them with Promises, then I turn on or off a variable in the state and it works. but I can not replicate that behavior in the navigation between pages and listen to load all the components
EDIT
Finally I achieved the desired behavior with a little trick I found on the internet
mounted() {
setTimeout(() => {
this.isBussy = false
}, 500)
},
this causes that the rendering of all the children components is delayed only a little, so that the activity indicator is shown, but not too much to produce that none of the components contained in the else block is detected and begin to rendering
There are two main ideas to understand here I think. I'll describe both.
1. General technique to Fetch Data without blocking render
It sounds like you understand this concept at the parent component level but then are asking how to do something very similar for the child components that this page contains.
The way I handle this, is in my component, I have my data default to an isLoading state. Then, in beforeMount() or mounted(), I perform my asynchronous actions and make necessary changes to my page's data.
The problem becomes entirely recursive when we look at child components. You want to make sure your child components are rendering and that any long running data fetching that needs to occur within their implementation will simply cause them to re-render once that fetching is complete.
Here is a working example: https://codesandbox.io/embed/r4o56o3olp
This example uses Nuxt. Aside from the addition fetch() and asyncData() methods, the rest of the Vue lifecycle hooks are the same here.
I use new Promise and setTimeout to demonstrate an operation that would use promises and be asynchronous. (e.g. axios.get(..))
The About page loads, and the beforeMount() lifecycle hook performs the asynchronous fetching in a way that doesn't block the page from rendering.
I use the beforeMount() hook because, according to here ( https://alligator.io/vuejs/component-lifecycle/ ), it is the first lifecycle hook that we have access to once the page's data is reactive. (So modifying this.myDataProp would trigger a re-render if {{ myDataProp }} was used in the template).
I also included a child component where I purposely made its data take twice as long to load. Since I again, am letting the component render immediately, and then I handle the fetching/updating of data in an appropriate lifecycle hook, I can manage when the end-user perceives a page to be loaded.
In my working example, the LongLoadingComponent did the same exact technique as the About page.
Once you see how to use beforeMount() or mounted() to fetch data and then update state, I think the trick is to take a moment and really think about the default state of your component. When it first renders, what should the user see before any of it's data fetching/long-running operations are completed?
Once you determine what your default (not yet loaded) component should look like, try getting that to render on your screen, and secondarily add in the logic that fetches and updates state data.
2. Listening for when a Child Component is finished rendering from a parent component
This makes use of the above technique, but includes the usage of the updated() hook and emitting a custom event ( https://v2.vuejs.org/v2/guide/components-custom-events.html
)
If you really want to listen for when your child components are finished rendering, you can $emit a custom event in your updated() hook. Perhaps something like this (in one of your child components)
if (this.dataLoaded) { this.$emit('loadedAndRendered') }
So when the child's async operations are done, it can flip it's dataLoaded property to true. If dataLoaded is used in the child's <template> somewhere, then the component should re-render (for it's "finished" state). When the child re-renders, the updated() hook should trigger. (again, see: https://alligator.io/vuejs/component-lifecycle/ ) I included the if (this.dataLoaded) part just to handle case where updated() hook might be called during intermediate data updates. (We only want to emit loadedAndRendered event if child is finished loading data/updating.)
3. Other caveats about universal nuxt applications
It wasn't until after I wrote this answer that I realized you aren't using Nuxt. However I'm adding this in case other Nuxt users happen to come across this.
I'm adding this section just because it took some focused hands-on time for me to wrap my head around. A Nuxt Universal Application does both server-side and client-side rendering. Understanding when something renders on the client vs when it was rendered on the server was a little difficult for me at first. In the working example I linked above, when you visit the about page you can also see if that component was fetched from the server or if it was just rendered by the client.
I'd recommend playing with a Page's fetch() and asyncData() methods and see how it impacts when certain things render on your screen. ( https://nuxtjs.org/api/pages-fetch/ ) ( https://nuxtjs.org/api/ ). Seeing what these methods are useful for helps me also identify what they are not useful for.
If you're using a Vuex store, I'd recommend seeing what happens when you refresh a page or use instead of a to navigate between pages. (Seeing something like the SSR schema diagram can be helpful here: https://nuxtjs.org/guide#schema )
..I have yet to fully appreciate the details of the bundling and delivery behavior that Webpack provides for a Universal Nuxt app (See right side of diagram here: https://medium.freecodecamp.org/universal-application-code-structure-in-nuxt-js-4cd014cc0baa )
My app uses a react-navigation DrawerNavigator component to allow the user to navigate through various screens within the app.
My react-native-maps MapView component is nested inside a screen accessible via the DrawerNavigator.
The problem I am finding is that if you navigate to another page in the app, and then navigate back to the map page, the whole map has to reload and previous markers/map configuration is lost.
Is there a way that I can prevent the screen from unmounting when navigating away, or another way of stopping the whole map from resetting? I won't post code below because I believe the issue to be more theory based as opposed to fixing a code bug.
You need to persist the state when the component is unmounted. You need a state management library.
I know of two state management libraries.
RxJS is the recommended library for use with Angular. Even though it is not an developed by Angular, it is still installed by default if you use the Angular CLI to bootstrap a project. This library is incredibly powerful, especially with handling asynchronous data flows, and it fits in really well with the angular DI system. My understanding is that you create singleton services to manage particular parts of your global state. You could have many RxJS services for different parts of your app. Your components can then tap into these services and get state information from them. There are libraries which help you integrate RxJS with react components but I cannot attest to their value.
Redux is the canonical way to manage global and persisted state in React. It differs from RxJS in many ways. First, you have only one redux store in your whole app and it contains the entire global state. Second, Redux is modeled on Flux and setting up the various 'players' for the first time can be a very involved process (but once you get it it's easy). I highly recommend making use of the combineReducers function to simplify getting set up. Third, redux does not manage async data straight out of the box, you will need to reach for redux-thunkif you have async data flows.
Redux is still my go-to for global and persisted state in react because of how it integrates. There is a library called react-redux which integrates the two libraries really well. It provides you with a function called connect. The connect function accesses your global state and passes it into your components as a prop.
You wrap your entire app in a store provider line so
export default () => {
<Provider store={store}>
<App />
</Provider>
Then your individual components can access state using connect. connect accepts a function which extracts parts of your state for you. The function could look like this.
const mapStateToProps = state => {
return {
stateVariable: state.variable
}
Now you know your component will receive a prop called stateVariable which is the value of variable in your global store / state. So you can write your component to accept this prop
class Component extends React.Component {
render() {
var { stateVariable} = this.props;
return (
<View>
<Text>{stateVariable}</Text>
</View>
)
}
Then you call connect on your component with the mapStateToProps function and hey presto
const ConnectedComponent = connect(mapStateToProps)(Component)
export { ConnectedComponent as Component }
You see how this injects the props as if you had written
<Component stateVariable={state.variable} />
In this way it is a solution to prop-drilling
In addition, you can use redux-persist to persist state between sessions, not just mounting/unmounting components. This library accesses localStorage on web or asyncStorage on native.
When you call connect on a component is automatically passes in a prop called dispatch. Dispatch is a function which is used to dispatch actions which make edits to your local store. as I said the system requires some setting up - you must create constants, actions-creators, and reducers to manage these action dispatches. If you watch the first 8 videos of this course you will be well on your way https://egghead.io/courses/getting-started-with-redux
At this moment in time my recommendation is to use Redux with React.