Showing posts with label reactjs. Show all posts
Showing posts with label reactjs. Show all posts

Saturday, December 26, 2020

Handle click outside of React component | ReactJS Component Detect Click Outside Listener of HTML Element

Handle click outside of React component | ReactJS Component Detect Click Outside Listener of HTML Element

This will be helpful when we need to check click outside event for multiple elements on same component
The first thing you need to know is that you can attach and detach mousedown listeners on the document object itself.
This is how we will attach mousedown listener for our component.
Using this function we will let parent component that some click event triggered out of the element passed to component so parent class can take some action when click outside happened.
And we will get notified when outside click triggered of an specific element.
import React, { Component } from "react";
import "./styles.css";

class OutsideClickCheckHandler extends Component {
  constructor(props) {
    super(props);

    this.parent = props.parent;
    this.tag = props.tag;
    this.classToCHeck =
      "outside_click_check_handler_" + ++OutsideClickCheckHandler.counter;
    this.setWrapperRef = this.setWrapperRef.bind(this);
    this.handleClickOutside = this.handleClickOutside.bind(this);
  }

  componentDidMount() {
    document.addEventListener("mousedown", this.handleClickOutside);
  }

  componentWillUnmount() {
    document.removeEventListener("mousedown", this.handleClickOutside);
  }

  setWrapperRef(node) {
    this.wrapperRef = node;
    if (node !== undefined && node !== null) {
      node.classList.add(this.classToCHeck);
    }
  }

  handleClickOutside(e) {
    if (!e.target.closest("." + this.classToCHeck)) {
      this.parent.someOneClickedOutsideMe(this.tag);
    }
  }

  render() {
    return <div ref={this.setWrapperRef}>{this.props.children}</div>;
  }
}

export default OutsideClickCheckHandler;

if (OutsideClickCheckHandler.counter === undefined) {
  OutsideClickCheckHandler.counter = 0;
}
This is how will receive notification when outside click triggered
This is how we will use component to detect oustide click of an element, you can set multiple child component from a single component with different tag so that you know which element is clicked outside.
Full example at CodeSandBox.IO

Sunday, July 12, 2020

How to remove build files before another new build start on Laravel-Mix Webpack config file | Delete unused laravel chunk files | Clean dist folder before generating a new build - Laravel with ReactJS

Since we generate lot of builds with different hashed filenames, it is a good practice to delete the dist directory before running each build.
In laravel-mix webpack.mix.js is the configuration file we used for reactjs webpack configuration. What we have to do write some code to webpack.mix.js (located in based directory of project in my case)
const mix = require('laravel-mix');
const date = (new Date()).getTime();

const fs = require('fs');
const buildDir = './public/js/chunk/';
fs.readdir(path.resolve(buildDir), (err, files) => {
    if (err) {
        console.log(err);
    }
    else {
        files.forEach(function (file) {
            fs.unlink(path.resolve(buildDir + file), function () {
                console.log(buildDir + file + ' - deleted');
            });
        });
    }
});

/*
 |--------------------------------------------------------------------------
 | Mix Asset Management
 |--------------------------------------------------------------------------
 |
 | Mix provides a clean, fluent API for defining some Webpack build steps
 | for your Laravel application. By default, we are compiling the Sass
 | file for the application as well as bundling up all the JS files.
 |
 */

mix.react('resources/js/app.js', 'public/js')
   .sass('resources/sass/app.scss', 'public/css');

mix.webpackConfig({
    output: {
        // Directory for junk files to {ROOT_DIR}/public/js
        chunkFilename: 'js/chunk/[name]-' + date + '.js'
    },
});

Saturday, March 14, 2020

Laravel-mix Webpack Public Path | Set custom path for chunk files | Using Laravel Mix with webpack for All Your Assets

Laravel Mix allows you to use a single line to describe what you want and it'll use it's preconfigured settings to process it properly.
Most of our time will be spent in the webpack.mix.js file. In the file, we can configure all we need to do to set public path and we can also set js file location for chunk js files when we use Suspense and lazy for dynamic js loading for per route.
There are several more Mix methods and you can set all of them in the default webpack.mix.js file.
// Full API
    // mix.js(src, output);
    // mix.react(src, output); <-- Identical to mix.js(), but registers React Babel compilation.
    // mix.extract(vendorLibs);
    // mix.sass(src, output);
    // mix.standaloneSass('src', output); <-- Faster, but isolated from Webpack.
    // mix.fastSass('src', output); <-- Alias for mix.standaloneSass().
    // mix.less(src, output);
    // mix.stylus(src, output);
    // mix.postCss(src, output, [require('postcss-some-plugin')()]);
    // mix.browserSync('my-site.dev');
    // mix.combine(files, destination);
    // mix.babel(files, destination); <-- Identical to mix.combine(), but also includes Babel compilation.
    // mix.copy(from, to);
    // mix.copyDirectory(fromDir, toDir);
    // mix.minify(file);
    // mix.sourceMaps(); // Enable sourcemaps
    // mix.version(); // Enable versioning.
    // mix.disableNotifications();
    // mix.setPublicPath('path/to/public');
    // mix.setResourceRoot('prefix/for/resource/locators');
    // mix.autoload({}); <-- Will be passed to Webpack's ProvidePlugin.
    // mix.webpackConfig({}); <-- Override webpack.config.js, without editing the file directly.
    // mix.then(function () {}) <-- Will be triggered each time Webpack finishes building.
    // mix.options({
    //   extractVueStyles: false, // Extract .vue component styling to file, rather than inline.
    //   processCssUrls: true, // Process/optimize relative stylesheet url()'s. Set to false, if you don't want them touched.
    //   purifyCss: false, // Remove unused CSS selectors.
    //   uglify: {}, // Uglify-specific options. https://webpack.github.io/docs/list-of-plugins.html#uglifyjsplugin
    //   postCss: [] // Post-CSS options: https://github.com/postcss/postcss/blob/master/docs/plugins.md
    // });
Below is the code snippet which will define js file location for chunk js files:
const mix = require('laravel-mix');

/*
 |--------------------------------------------------------------------------
 | Mix Asset Management
 |--------------------------------------------------------------------------
 |
 | Mix provides a clean, fluent API for defining some Webpack build steps
 | for your Laravel application. By default, we are compiling the Sass
 | file for the application as well as bundling up all the JS files.
 |
 */

mix.react('resources/js/app.js', 'public/js')
   .sass('resources/sass/app.scss', 'public/css');

mix.webpackConfig({
    output: {
        // Directory for junk files to {ROOT_DIR}/public/js
        chunkFilename: 'js/[name].js',
    },
});

Thursday, March 12, 2020

React Component on Window Resize Event

React doesn't have a resize event integrated into it, but we can listen to the native browser window resize event from within our React component. We will attach window resize event into componentDidMount method.
Under normal conditions, React will re-render a component when its props or state changes. To trigger a re-render of Component in the example, we'll set internal state on the component when the event is fired. So we can set/update state variable to tell React Component to update. We can implement method componentDidUpdate to acknoledged something changed.
When adding an event listener, such as we are for the resize event, we should make sure to clean up after ourselves. We will clean up any type of listener we added into method componentWillUnmount.
Example code given below:
import React from "react";
import "./styles.css";

class App extends React.Component {
  constructor() {
    super();

    // we will update below state variables to tell react component
    // to update, only state or props change can update react component
    this.state = {
      width: 0,
      height: 0
    };

    // we will use this timer to less fire resize event
    // practially we will fire resize event 500 miliseconds after
    this.timer = null;

    // we will catch windows resize event into below function
    this.windowResized = this.windowResized.bind(this);

    // we will handle resize event so resize event will
    // be in some time interval, less freequent 
    this.updateWindowWidth = this.updateWindowWidth.bind(this);
  }

  componentDidMount() {
    window.addEventListener("resize", this.windowResized);
    this.updateWindowWidth();
  }

  componentDidUpdate() {
    console.log(
      `Window width/height changed to ${this.state.width}X${this.state.height}`
    );
  }

  componentWillUnmount() {
    window.removeEventListener("resize", this.windowResized);
  }

  updateWindowWidth() {
    let _this = this;
    setTimeout(function() {
      _this.setState({
        width: window.innerWidth,
        height: window.innerHeight
      });
    });
  }

  windowResized() {
    let _this = this;
    if (this.timer) {
      clearTimeout(this.timer);
    }
    this.timer = setTimeout(function() {
      _this.updateWindowWidth();
    }, 500);
  }

  render() {
    return (
      <div className="App">
        <h1>
          WIDTH={this.state.width} HEIGHT={this.state.height}
        </h1>
      </div>
    );
  }
}

export default App;
Sample Image:

Live Example:

https://codesandbox.io/s/react-component-on-window-resize-event-3b29u

Saturday, March 7, 2020

Lazy Loading React Components (with react.lazy and suspense)

Our first step is to create an React application using below command:

npm init react-app react-lazy-loading-component

Navigate to project folder and type npm start to start project on browser.

Make sure you execute command npm run build to prepare build script which will be deployed on server

Route-based code splitting

You have to install react-router-dom using npm install --save react-router-dom to use this functionality.

Deciding where in your app to introduce code splitting can be a bit tricky. You want to make sure you choose places that will split bundles evenly, but won’t disrupt the user experience.

A good place to start is with routes. Most people on the web are used to page transitions taking some amount of time to load. You also tend to be re-rendering the entire page at once so your users are unlikely to be interacting with other elements on the page at the same time.
Sample App.js

import {BrowserRouter as Router, Route, Switch} from 'react-router-dom';
import React, {Suspense, lazy} from 'react';
import './App.css';

import Header from './Header';

const Home = lazy(() => import('./Home'));
const AnotherHome = lazy(() => import('./AnotherHome'));

function App() {
    return (
        <div>
            <Router>
                <Header/>
                <div className="container">
                    <Suspense fallback={<div>Loading...</div>}>
                        <Switch>
                            <Route exact path="/" component={Home}/>
                            <Route exact path="/another-home" component={AnotherHome}/>
                        </Switch>
                    </Suspense>
                </div>
            </Router>
        </div>
    );
}

export default App;
Actually use of Suspense and Lazy will load components chunk by chunk when they needed, and once a component loaded into browser will not load second time.
GitHub Link https://github.com/pritomkucse/react-lazy-loading-component
Live Example at codesandbox.io

https://codesandbox.io/s/lazy-loading-react-components-with-reactlazy-and-suspense-2hx75

Make sure you see console to ensure that suspense callback working as expected:


Thursday, February 20, 2020

ReactJS Call Methods / Functions Between Component And Layout

In order to execute a function from a layout / component, you will need to pass component / layout reference to other side. React supports a special attribute that you can attach to any component, pass reference to other side, and you can access the functions of the component in the layout accessing
Sample App.js
import React, { Suspense, lazy } from "react";
import { BrowserRouter, Switch } from "react-router-dom";
import DashboardLayoutRoute from "./DashboardLayout";

import Home from "./Home";

const Connections = lazy(() => import("./Connections"));

class App extends React.Component {
  constructor() {
    super();

    console.log("App Initialized");
  }

  render() {
    return (
      <BrowserRouter basename={this.APP_PATH}>
        <Switch>
          <DashboardLayoutRoute exact path="/" component={Home} />
        </Switch>
        <Suspense fallback={<div>Loading...</div>}>
          <Switch>
            <DashboardLayoutRoute path="/connections" component={Connections} />
          </Switch>
        </Suspense>
      </BrowserRouter>
    );
  }
}

export default App;
Sample Layout
import React from "react";
import { Route } from "react-router-dom";

class DashboardLayoutComponent extends React.Component {
  constructor(props) {
    super(props);

    this.callBackToComponent = this.callBackToComponent.bind(this);
  }

  componentDidMount() {
    console.log("Layout Component Did Mount");
    let _this = this;
    if (this.props.children.type._result instanceof Promise) {
      this.props.children.type._result.then(function(data) {
        _this.callBackToComponent();
      });
    } else {
      this.callBackToComponent();
    }
  }

  componentDidUpdate() {
    console.log("Layout Component Did Update");
    let _this = this;
    if (this.props.children.type._result instanceof Promise) {
      this.props.children.type._result.then(function(data) {
        _this.callBackToComponent();
      });
    } else {
      this.callBackToComponent();
    }
  }

  callBackToComponent() {
    let t = this.props.children.type;
    if (t.prototype !== undefined) {
      t.prototype.callBackToParentComponent(this);
    } else {
      // If you use your Route in Suspense
      t._result.prototype.callBackToParentComponent(this);
    }
  }

  callbackParent() {
    console.log("Layout function called from some component");
  }

  render() {
    return (
      <div className={"dashboard-main-container"}>
        <div className="container">
          <div className="row">
            <div className="col-sm-10 right-body-container">
              {this.props.children}
            </div>
          </div>
        </div>
      </div>
    );
  }
}

const DashboardLayoutRoute = ({ component: Component, ...rest }) => {
  return (
    <Route
      {...rest}
      render={matchProps => (
        <DashboardLayoutComponent {...matchProps}>
          <Component key={matchProps.match.params.id} {...matchProps} />
        </DashboardLayoutComponent>
      )}
    />
  );
};

export default DashboardLayoutRoute;
Sample Component
import React from "react";
import { Link } from "react-router-dom";

class Connections extends React.Component {
  constructor() {
    super();

    console.log("Connections");
  }

  // this function will be called from layout function with self reference
  // so calling [layout.callbackParent()] will call a function inside layout component itself
  callBackToParentComponent(layout) {
    console.log("callBackToParentComponent for Connections");
    layout.callbackParent();
  }

  render() {
    return (
      <div>
        <h1>Connections</h1>
        <Link to={"/"}>Home</Link>
      </div>
    );
  }
}

export default Connections;
Sample console.log
App Initialized 
App Initialized 
Layout Component Did Mount 
callBackToParentComponent for Connections 
Layout function called from some component 
Connections 
Connections 
Home 
Home 
Layout Component Did Mount 
callBackToParentComponent for Home 
Layout function called from some component 
Connections 
Connections 
Layout Component Did Mount 
callBackToParentComponent for Connections 
Layout function called from some component 
Sample Output:
Live Example At codesandbox.io:

https://codesandbox.io/s/reactjs-call-methods-functions-between-component-and-layout-fydou

Saturday, February 15, 2020

How to write a simple React plugin, publish it to npm, and deploy it to Github pages

Getting started


Our target is to create and publish a ReactJS plugin so we can access it via NPM directly.

I'm going to creat react plugin using create-react-library which is a CLI for easily creating reusable react libraries. This CLI has a bunch of features and will help us in generating a full featured project as well as plugin.

To use create-react-library, we’ll need to install it globally:

npm install -g create-react-library


The above command will install create-react-library globally and we can generate a new module from any directory.

Now execute following command to create an module which will ask you some basic questions about your plugin and will take few minutes to complete installation of reactjs plugin:

create-react-library



After plugin project created navigate to project directory using

cd react-profile-image-box


Now, you need to run the plugin (for watching any changes that you make to it) and the example. In one tab, you can run:

npm start


And, in another tab, you need to run the example app:

cd ./example && npm start


The last command will run a create-react-app project which imports your plugin. If you make any changes to your plugin, it will get reflected in the example app. You can view the current status of your plugin by visiting http://localhost:3000.

import React, { Component } from 'react'
import PropTypes from 'prop-types'

import styles from './styles.css'

export default class ProfileImageBox extends Component {
  /**
     * src: The src for img tag
     * alt: Alt text for img tag
     * allowUpload: Boolean field to set if image can be changed or not
     * onFileChange: A function that will receive file change event
     */
  static propTypes = {
    src: PropTypes.string,
    alt: PropTypes.string,
    allowUpload: PropTypes.bool,
    onFileChange: PropTypes.func
  }

  static defaultProps = {
    allowUpload: false,
    onFileChange: function(e, additionalParams) {
      alert("You need to implement onFileChange(e) method")
    }
  }

  constructor (props) {
    super(props)
  }

  render() {
    const {
      src, alt, allowUpload, onFileChange
    } = this.props

    return (
      <div className={styles.react_profile_image_box_container}>
        {allowUpload ? (<input 
          className={styles.react_profile_image_box_upload_profile_image_input}
          onChange={(e) => onFileChange(e)} 
          type="file"/>) : ''}
        
        <img 
          className={styles.react_profile_image_box_rounded_circle} 
          alt={alt} 
          src={src} 
          data-holder-rendered="true"/>
      </div>
    )
  }
}

You should use above plugin as below:

import React, { Component } from 'react'

import ProfileImageBox from 'react-profile-image-box'

export default class App extends Component {
  state = {
    src: "http://test.com/avatar_images_by_user/72"
  }

  onFileChange(e, additionalParams) {
    console.log(e.target.files);
    console.log(additionalParams);
    this.setState({src: "http://test.com/avatar_images_by_user/70"});
    // YOU SHOULD UPLOAD YOUR FILE FROM HERE
    // AND SET SCR AGAIN TO SEE LATEST PRICTURE
  }

  render () {
    return (
      <div>
        <ProfileImageBox 
          alt="Alt Text" 
          allowUpload={true} 
          onFileChange={(e) => this.onFileChange(e, {type: 'user-image'})} 
          src={this.state.src}/>
      </div>
    )
  }
}

You can now push your code to git repository

Our main target is to publishing our plugin to npm


create-react-library already has a feature through which we can deploy the example folder to Github pages. You just need to run the following command:

Be sure run npm login to login to npm server and then run npm publish.


Npm package is published now https://www.npmjs.com/package/react-profile-image-box