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 |
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
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 startedOur 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-libraryThe 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-boxNow, 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 startAnd, in another tab, you need to run the example app: cd ./example && npm startThe 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 npmcreate-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 |
Subscribe to:
Posts (Atom)