How to import a component later? - reactjs

I'm trying to import components with a delay.
I want to import the components quietly.
While looking at the home page, the back components will load silently.
I tried lazy loading, but this option returned me the page every time I logged in for a longer time.
const SignUp = lazy(async () => {
await new Promise(resolve => setTimeout(resolve, 5000));
return import('../sign-up/sign-up').then(({ SignUp }) => ({ default: SignUp }));});
How can I solve it?
thanks.

So there are three ways to lazy load component you can check my repo here https://github.com/amansadhwani/all-features-react/tree/master/src/components/LazyLoading
/* 1)Naive lazy load */
const Chart = lazy(() => import("./AnyComponent")); // this will load on demand when needed
/*2) Lazy load with prefetch */
const Chart = lazy(() => import(/* webpackPrefetch: true */ "./AnyComponent")); // this will load after everything is completed in short at the end of everything it will load
/*3) Predictive lazy load */
const loadComponent = () => import("./AnyComponent");
const Comp= lazy(loadComponent ); // this is something you will load as soon as someone hover's over any button you will attach event listners onFocus and onMouseEnter as shown below
onFocus={Comp}
onMouseEnter={Comp}

React.lazy() is a new function in react that lets you load react components lazily through code splitting without help from any additional libraries. Lazy loading is the technique of rendering only-needed or critical user interface items first , then quietly unrolling the non-critical items later.
Suspense is a component required by the lazy function basically used to wrap lazy components.
Solution for Using React Suspense (React 16.6+) version
import React, { Suspense } from "react";
const Customer = React.lazy(() => import("./Customer.js"));
const Admin = React.lazy(() => import("./Admin.js"));
//Instead of regular import statements, we will use the above approach for lazy loading
export default (props) => {
if (props.user === "admin") {
return (
// fallback component is rendered until our main component is loaded
<Suspense fallback={<div>Loading</div>}>
<Admin />
</Suspense>
);
} else if (props.user === "customer") {
return (
<Suspense fallback={<div>Loading</div>}>
<Customer />
</Suspense>
);
} else {
return <div> Invalid User </div>;
}
};
Without React Suspense
Our calling component, in this case Home.js
import React from "react";
import { lazyLoader } from "./lazyLoader";
const Customer = lazyLoader(() => import("./Customer.js"));
const Admin = lazyLoader(() => import("./Admin.js"));
//Instead of regular import statements, we will use the above approach for lazy loading
export default (props) => {
if (props.user === "admin") {
return <Admin />;
} else if (props.user === "customer") {
return <Customer />;
} else {
return <div> Invalid User </div>;
}
};
And lazyLoader.js
const lazyLoader = (importComp, fallback) => {
return class extends React.Component {
state = {
component: null, //initializing state
};
//loading the component and setting it to state
componentDidMount() {
importComp().then((comp) => setState({ component: comp.default }));
}
//rendering the component
render() {
const C = this.state.component;
return C ? (
<C {...this.props} />
) : fallback ? (
fallback
) : (
<div>loading</div>
);
// If component is not loaded then return fallback component, if fallback is not provided then use default loading
}
};
};
export default lazyLoader;

Related

How to use lazy loading in data-grid material-ui

When I import data-grid via lazy loading then the error came.
const DataGrid = lazy(async () => await import('#material-ui/data-grid'))
Please tell me whether I am importing correctly because when I import other material-ui components then that component works fine but for data-grid import it occurs an error.
DataGrid is not a default export, so try
const DataGrid = React.lazy(
() => import('#material-ui/data-grid').then(module => ({ default: module.DataGrid }))
);
React.lazy takes a function that must call a dynamic import(). This
must return a Promise which resolves to a module with a default export
containing a React component.
For me the below code works fine, based on answer from #Someone Special
import React, { lazy, Suspense } from 'react';
const loadable = (importFunc, { fallback = null } = { fallback: null }) => {
const LazyComponent = lazy(importFunc);
return props => (
<Suspense fallback={fallback}>
<LazyComponent {...props} />
</Suspense>
);
};
export default loadable;
import React, { Suspense } from 'react';
import { Loading } from 'dan-components';
import loadable from '../../utils/loadable';
const DataGrid = loadable(() =>
import('#material-ui/data-grid').then(module => {
return { default: module.DataGrid };
}),
);
const LazyDataGrid = props => {
return (
<Suspense fallback={<Loading />}>
<DataGrid {...props} />
</Suspense>
);
};
export default LazyDataGrid;

How can i use react-toastify from hook?

I found useToast and useToastContainer, but documentation is absent and i don't understand how tu use these hooks. Can anyone provide some info about these hooks?
The toasts inherit ToastContainer’s props. Props defined on toast supersede ToastContainer’s props.
There are two ways you can use toasts in your application:
1. Define ToastContainer inside the component
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
const App = () => {
notify = () => toast("Wow so easy !");
return (
<div>
<button onClick={notify}>Notify !</button>
// You can add <ToastContainer /> in root component as well.
<ToastContainer />
</div>
);
}
2. Call toast.configure() once in your app. At the root of your app is the best place.
The library will mount a ToastContainer for you if none is mounted.
import { toast } from "react-toastify";
import 'react-toastify/dist/ReactToastify.css';
// Call it once in your app. At the root of your app is the best place
toast.configure()
const App = () => {
notify = () => toast("Wow so easy !");
return (
<button onClick={notify}>Notify !</button>
);
}
You can use either of them. I prefer the 2nd method because you only need to define toast.configure() which is quite clean way to add it.
You can add configuration as per your need like below:
toast.configure({
autoClose: 8000,
draggable: false,
//etc you get the idea
});
EDIT
If you want to use toast hooks, then you must wrap your app with the ToastProvider to have access to its context elsewhere within your app.
import { ToastProvider, useToasts } from 'react-toast-notifications'
const FormWithToasts = () => {
const { addToast } = useToasts()
const onSubmit = async value => {
const { error } = await dataPersistenceLayer(value)
if (error) {
addToast(error.message, { appearance: 'error' })
} else {
addToast('Saved Successfully', { appearance: 'success' })
}
}
return <form onSubmit={onSubmit}>...</form>
}
const App = () => (
<ToastProvider>
<FormWithToasts />
</ToastProvider>
)

React Native - reduce render times to optimize performance while using React hooks

Background
After releasing React v16.8, now we have hooks to use in React Native.
I am doing some simple tests to see the render times and the performance between
Hooked functional components and class components. Here is my sample:
#Components/Button.js
import React, { memo } from 'react';
import { TouchableOpacity, Text } from 'react-native';
const Button = memo(({ title, onPress }) => {
console.log("Button render"); // check render times
return (
<TouchableOpacity onPress={onPress} disabled={disabled}>
<Text>{title}</Text>
</TouchableOpacity>
);
});
export default Button;
#Contexts/User.js
import React, { createContext, useState } from 'react';
import User from '#Models/User';
export const UserContext = createContext({});
export const UserContextProvider = ({ children }) => {
let [ user, setUser ] = useState(null);
const login = (loginUser) => {
if (loginUser instanceof User) { setUser(loginUser); }
};
const logout = () => {
setUser(null);
};
return (
<UserContext.Provider value={{value: user, login: login, logout: logout}}>
{children}
</UserContext.Provider>
);
};
export function withUserContext(Component) {
return function UserContextComponent(props) {
return (
<UserContext.Consumer>
{(contexts) => <Component {...props} {...contexts} />}
</UserContext.Consumer>
);
}
}
Cases
We have two cases below for constructing screen components:
#Screens/Login.js
Case 1: Functional Component with Hooks
import React, { memo, useContext, useState } from 'react';
import { View, Text } from 'react-native';
import Button from '#Components/Button';
import { UserContext } from '#Contexts/User';
const LoginScreen = memo(({ navigation }) => {
const appUser = useContext(UserContext);
const [foo, setFoo] = useState(false);
const userLogin = async () => {
let response = await fetch('blahblahblah');
if (response.is_success) {
appUser.login(user);
} else {
// fail on login, error handling
}
};
const toggleFoo = () => {
setFoo(!foo);
console.log("current foo", foo);
};
console.log("render Login Screen"); // check render times
return (
<View>
<Text>Login Screen</Text>
<Button onPress={userLogin} title="Login" />
<Button onPress={toggleFoo} title="Toggle Foo" />
</View>
);
});
export default LoginScreen;
Case 2: Component wrapped with HOC
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import Button from '#Components/Button';
import { withUserContext } from '#Contexts/User';
import UserService from '#Services/User';
class LoginScreen extends Component {
state = { foo: false };
userLogin = async () => {
let response = await UserService.login();
if (response.is_success) {
login(user); // function from UserContext
} else {
// fail on login, error handling
}
};
toggleFoo = () => {
const { foo } = this.state;
this.setState({ foo: !foo });
console.log("current foo", foo);
};
render() {
console.log("render Login Screen"); // check render times
return (
<View>
<Text>Login Screen</Text>
<Button onPress={userLogin} title="Login" />
<Button onPress={toggleDisable} title="Toggle" />
</View>
);
}
}
Results
Both cases have identical render times at the beginning:
render Login Screen
Button render
Button render
But while I press the "Toggle" button, the state changed and here is the result:
Case 1: Functional Component with Hooks
render Login Screen
Button render
Button render
Case 2: Component wrapped with HOC
render Login Screen
Questions
Although the Button Component isn't a large bunch of codes, considering the re-render times between two cases, Case 2 should have a better performance than Case 1.
However, considering the code readability, I definitely love using hooks more than using HOC. (Especially the function: appUser.login() and login())
So here's the question. Is there any solution that I can keep the benefits of both size, decreasing the re-render times while using the hooks? Thank you.
The reason that both button re-render even though you use memo in case of a functional component is because the function references are changed on each re-render as they are defined within the functional component.
Similar case will happen if you use arrow functions in render for a class component
In case of a class the function references don't change with the way you define them as functions are defined outside of your render method
To optimize on rerenders, you should make use of useCallback hook to memoize your function references
const LoginScreen = memo(({ navigation }) => {
const appUser = useContext(UserContext);
const [foo, setFoo] = useState(false);
const userLogin = useCallback(async () => {
let response = await fetch('blahblahblah');
if (response.is_success) {
appUser.login(user);
} else {
// fail on login, error handling
}
}, []); // Add dependency if need i.e when using value from closure
const toggleFoo = useCallback(() => {
setFoo(prevFoo => !prevFoo); // use functional state here
}, []);
console.log("render Login Screen"); // check render times
return (
<View>
<Text>Login Screen</Text>
<Button onPress={userLogin} title="Login" />
<Button onPress={toggleFoo} title="Toggle Foo" />
</View>
);
});
export default LoginScreen;
Also note that React.memo cannot prevent re-renders due to context value changes. Also note that while passing value to context provider you should make use of useMemo too
export const UserContextProvider = ({ children }) => {
let [ user, setUser ] = useState(null);
const login = useCallback((loginUser) => {
if (loginUser instanceof User) { setUser(loginUser); }
}, []);
const logout = useCallback(() => {
setUser(null);
}, []);
const value = useMemo(() => ({
value: user,
login: login,
logout: logout,
}), [user, login, logout]);
/*
Note that login and logout functions are implemented using `useCallback` and
are created on initial render only and hence adding them as dependency here
doesn't make a difference and will definitely not lead to new referecne for
value. Only `user` value change will create a new object reference
*/
return (
<UserContext.Provider value={value}>
{children}
</UserContext.Provider>
);
};
The reason is in functional component whenever the component re-render, new userLogin created => Button component is re-render.
const userLogin = async () => {
const response = await fetch("blahblahblah")
if (response.is_success) {
appUser.login(user)
} else {
// fail on login, error handling
}
}
You can use useCallback to memoize userLogin function + wrap Button component with React.memo (as what you did) prevent unwanted re-render:
const userLogin = useCallback(async () => {
const response = await fetch("blahblahblah")
if (response.is_success) {
appUser.login(user)
} else {
// fail on login, error handling
}
}, [])
The reason why it not happen in class component is when class component is re-rendered only render function is trigger (of course some other lifecycle function such as shoudlComponentUpdate, componentDidUpdate trigger too). ==> userLogin not change ==> Button component is not re-render.
This is great article to have a look about useCallback + memo
Notice: When you use Context, memo can not prevent the component, which is a Consumer, re-render if values of Context Provider changed.
For example:
If you call setUser in UserContext => UserContext re-render => value={{value: user, login: login, logout: logout}} change => LoginScreen re-render. You cannot use shouldComponentUpdate (class compoenent) or memo (functional component) to prevent re-render, because it's not update via props, it's updated via value of Context Provide

React - Wait for complex method to finish before rendering

I'm trying to display a dashboard component, crunching a lot of data fetched from my redux store. This component takes a lot of time to render, mainly because of a single complex method.
Is it possible to render some kind of loader or placeholder while this method is processing ?
I tried doing so by using ComponentDidMount, but it seems like, because the method is part of my render() method, it will always be triggered first-hand.
Yes! Check out this tutorial.
Loader:
import React, {Component} from 'react';
const asyncComponent = (importComponent) => {
return class extends Component {
state = {
component: null
}
componentDidMount() {
importComponent()
.then(cmp => {
this.setState({component: cmp.default});
});
}
render() {
const C = this.state.component;
return C ? <C {...this.props}/> : null;
}
}
};
export default asyncComponent;
Usage:
import React from 'react';
import asyncComponent from '../../hoc/asyncComponent';
const AsyncButton = asyncComponent(() => {
return import('../Button');
});
const container = () => {
return (
<div>
<h1>Here goes an async loaded button component</h1>
<AsyncButton/>
</div>
);
};
export default container;
or check out this library.

Using React context to maintain user state

I'm trying to use React's context feature to maintain information about the user throughout the application (e.g. the user ID, which will be used in API calls by various pages). I'm aware that this is an undocumented and not recommended over Redux, but my application is pretty simple (so I don't want or need the complexity of Redux) and this seems like a common and reasonable use case for context. If there are more acceptable solutions for keeping user information globally throughout the application, though, I'm open to using a better method.
However, I'm confused about how it's to be used properly: once the user logins in through the AuthPage (a child of the ContextProvider), how do I update the context in ContextProvider so it can get to other components, like the FridgePage? (Yes, context is technically not supposed to be updated, but this is a one-time operation -- if anyone knows a way to do this when ContextProvider is initialized, that would be more ideal). Does the router get in the way?
I've copied the relevant components here.
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { HashRouter, Route, Switch } from 'react-router-dom';
import Layout from './components/Layout.jsx';
import AuthPage from './components/AuthPage.jsx';
import ContextProvider from './components/ContextProvider.jsx';
ReactDOM.render(
<ContextProvider>
<HashRouter>
<Switch>
<Route path="/login" component={AuthPage} />
<Route path="/" component={Layout} />
</Switch>
</HashRouter>
</ContextProvider>,
document.getElementById('root')
);
ContextProvider.jsx
import React from 'react';
import PropTypes from 'prop-types';
export default class ContextProvider extends React.Component {
static childContextTypes = {
user: PropTypes.object
}
// called every time the state changes
getChildContext() {
return { user: this.state.user };
}
render() {
return(
<div>
{ this.props.children }
</div>
);
}
}
AuthPage.jsx
import React from 'react';
import PropTypes from 'prop-types';
import AuthForm from './AuthForm.jsx';
import RegisterForm from './RegisterForm.jsx';
import Api from '../api.js';
export default class AuthPage extends React.Component {
static contextTypes = {
user: PropTypes.object
}
constructor(props) {
super(props);
this.updateUserContext = this.updateUserContext.bind(this);
}
updateUserContext(user) {
console.log("Updating user context");
this.context.user = user;
console.log(this.context.user);
}
render() {
return (
<div>
<AuthForm type="Login" onSubmit={Api.login} updateUser={this.updateUserContext} />
<AuthForm type="Register" onSubmit={Api.register} updateUser={this.updateUserContext} />
</div>
);
}
}
Layout.jsx
import React from 'react';
import Header from './Header.jsx';
import { Route, Switch } from 'react-router-dom';
import FridgePage from './FridgePage.jsx';
import StockPage from './StockPage.jsx';
export default class Layout extends React.Component {
render() {
return (
<div>
<Header />
<Switch>
<Route exact path="/stock" component={StockPage} />
<Route exact path="/" component={FridgePage} />
</Switch>
</div>
);
}
}
FridgePage.jsx (where I want to access this.context.user)
import React from 'react';
import PropTypes from 'prop-types';
import Api from '../api.js';
export default class FridgePage extends React.Component {
static contextTypes = {
user: PropTypes.object
}
constructor(props) {
super(props);
this.state = {
fridge: []
}
}
componentDidMount() {
debugger;
Api.getFridge(this.context.user.id)
.then((fridge) => {
this.setState({ "fridge": fridge });
})
.catch((err) => console.log(err));
}
render() {
return (
<div>
<h1>Fridge</h1>
{ this.state.fridge }
</div>
);
}
}
Simple state provider
auth module provides two functions:
withAuth - higher order component to provide authentication data to components that need it.
update - function for updating authentication status
How it works
The basic idea is that withAuth should add auth data to props that are being passed to a wrapped component.
It is done in three steps: take props that being passed to a component, add auth data, pass new props to the component.
let state = "initial state"
const withAuth = (Component) => (props) => {
const newProps = {...props, auth: state }
return <Component {...newProps} />
}
One piece that is missing is to rerender the component when the auth state changes. There are two ways to rerender a component: with setState() and forceUpdate(). Since withAuth doesn't need internal state, we will use forceUpdate() for rerendering.
We need to trigger a component rerender whenever there is a change in auth state. To do so, we need to store forceUpdate() function in a place that is accesible to update() function that will call it whenever auth state changes.
let state = "initial state"
// this stores forceUpdate() functions for all mounted components
// that need auth state
const rerenderFunctions = []
const withAuth = (Component) =>
class WithAuth extends React.Component {
componentDidMount() {
const rerenderComponent = this.forceUpdate.bind(this)
rerenderFunctions.push(rerenderComponent)
}
render() {
const newProps = {...props, auth: state }
return <Component {...newProps} />
}
}
const update = (newState) => {
state = newState
// rerender all wrapped components to reflect current auth state
rerenderFunctions.forEach((rerenderFunction) => rerenderFunction())
}
Last step is to add code that will remove rerender function when a component is going to be unmounted
let state = "initial state"
const rerenderFunctions = []
const unsubscribe = (rerenderFunciton) => {
// find position of rerenderFunction
const index = subscribers.findIndex(subscriber);
// remove it
subscribers.splice(index, 1);
}
const subscribe = (rerenderFunction) => {
// for convinience, subscribe returns a function to
// remove the rerendering when it is no longer needed
rerenderFunctions.push(rerenderFunction)
return () => unsubscribe(rerenderFunction)
}
const withAuth = (Component) =>
class WithAuth extends React.Component {
componentDidMount() {
const rerenderComponent = this.forceUpdate.bind(this)
this.unsubscribe = subscribe(rerenderComponent)
}
render() {
const newProps = {...props, auth: state }
return <Component {...newProps} />
}
componentWillUnmount() {
// remove rerenderComponent function
// since this component don't need to be rerendered
// any more
this.unsubscribe()
}
}
// auth.js
let state = "anonymous";
const subscribers = [];
const unsubscribe = subscriber => {
const index = subscribers.findIndex(subscriber);
~index && subscribers.splice(index, 1);
};
const subscribe = subscriber => {
subscribers.push(subscriber);
return () => unsubscribe(subscriber);
};
const withAuth = Component => {
return class WithAuth extends React.Component {
componentDidMount() {
this.unsubscribe = subscribe(this.forceUpdate.bind(this));
}
render() {
const newProps = { ...this.props, auth: state };
return <Component {...newProps} />;
}
componentWillUnmoount() {
this.unsubscribe();
}
};
};
const update = newState => {
state = newState;
subscribers.forEach(subscriber => subscriber());
};
// index.js
const SignInButton = <button onClick={() => update("user 1")}>Sign In</button>;
const SignOutButton = (
<button onClick={() => update("anonymous")}>Sign Out</button>
);
const AuthState = withAuth(({ auth }) => {
return (
<h2>
Auth state: {auth}
</h2>
);
});
const App = () =>
<div>
<AuthState />
{SignInButton}
{SignOutButton}
</div>;
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
playground: https://codesandbox.io/s/vKwyxYO0
here is what i did for my project:
// src/CurrentUserContext.js
import React from "react"
export const CurrentUserContext = React.createContext()
export const CurrentUserProvider = ({ children }) => {
const [currentUser, setCurrentUser] = React.useState(null)
const fetchCurrentUser = async () => {
let response = await fetch("/api/users/current")
response = await response.json()
setCurrentUser(response)
}
return (
<CurrentUserContext.Provider value={{ currentUser, fetchCurrentUser }}>
{children}
</CurrentUserContext.Provider>
)
}
export const useCurrentUser = () => React.useContext(CurrentUserContext)
and then use it like this:
setting up the provider:
// ...
import { CurrentUserProvider } from "./CurrentUserContext"
// ...
const App = () => (
<CurrentUserProvider>
...
</CurrentUserProvider>
)
export default App
and using the context in components:
...
import { useCurrentUser } from "./CurrentUserContext"
const Header = () => {
const { currentUser, fetchCurrentUser } = useCurrentUser()
React.useEffect(() => fetchCurrentUser(), [])
const logout = async (e) => {
e.preventDefault()
let response = await fetchWithCsrf("/api/session", { method: "DELETE" })
fetchCurrentUser()
}
// ...
}
...
the full source code is available on github: https://github.com/dorianmarie/emojeet
and the project can be tried live at: http://emojeet.com/
You don't update the context, you update the ContextProvider's state which will re render the children and populate the context through getChildContext; in your context you can place functions that when called update the provider's state. Make sure you also create a high order component(HOC) named something like withAuthContext that would read the context and turned it into props for a child component to consume, much like withIntl from react-intl or withRouter from react-router among many others, this will make the development of your components simpler and context independent as if at some point you decide to just move to redux you won't have to deal with context just replace the HOC with connect and mapStateToProps.
I think I wouldn't use the context to achieve this.
Even if your app is simple (and I understand you don't want to use Redux), it's a good practice to separate the model from the view.
Consider implementing a very simple Flux architecture: create a store and dispatch actions every time you have to change the model (eg. storing user). Your views just have to listen for the store event and update their DOM.
https://facebook.github.io/flux/docs/in-depth-overview.html#content
Here's a boilerplate with a tiny helper to manage Flux : https://github.com/christianalfoni/flux-react-boilerplate/blob/master/package.json

Resources