How to add event handlers to none React parts of the page - reactjs

I have a dropdown box in a react component that expands/contracts to show the content. If the user clicks off the box (almost anywhere on the page), I want the box to contract. Without React, I would use an event handler to run some jQuery to contract the box, however my understanding is that this will cause issues since the jQuery will have changed the DOM and React will not be aware of it.
Can someone please explain how to add an event handler to an element that will cause a function inside React to run and contract the box.
I have tried the code below, however when I try to add the event listener i get the message that "this.myFunction" is undefined in my AddListener function.
import './App.css';
import React, { Component } from 'react';
//function App() {
class App extends Component {
constructor(props) {
super(props);
this.myFunction = this.myFunction.bind(this);
this.removeListener = this.removeListener.bind(this)
}
myFunction() {
alert("Hi user!");
}
removeListener() {
document.getElementById('CLICKER').removeEventListener("click", this.myFunction, false);
console.log('Removed');
}
addListener() {
document.getElementById('CLICKER').addEventListener('click', this.myFunction);
console.log('Added');
}
render() {
return (
<div className="App">
<div onClick={this.removeListener} style={{ backgroundColor: 'red' }}>
Remove Event Listener
</div>
<div onClick={this.addListener} style={{ backgroundColor: 'blue' }}>
Add Event Listener
</div>
</div>
);
}
}
export default App;

I realised i needed to add the constructor:
this.addListener = this.addListener.bind(this);

Related

Material UI snackbars not working properly in Next JS

I am trying to use Material UI components in NextJS but they are not working properly.
import React, { Component } from "react";
// MATERIAL
import Snackbar from "#material-ui/core/Snackbar";
import MuiAlert from "#material-ui/lab/Alert";
export default class NewProduct extends Component {
constructor() {
super();
this.state = {
testSnack: false,
};
}
testSnackbars = () => {
this.setState({ testSnack: true });
};
render(){
return{
<>
<Snackbar
open={this.state.testSnack}
autoHideDuration={5000}
onClose={this.handleClose}
>
<MuiAlert
elevation={6}
variant="filled"
onClose={this.handleClose}
severity="success"
>
Product Added!
</MuiAlert>
</Snackbar>
<button onClick={this.testSnackbars}>
Save
</button>
</>
}
}
}
This is a simple logic for showing snackbar on button click but this doesnt seem to work in NextJs, I use Material in ReactJs very often and this never happened there. Can someone tell me what I'm doing wrong here?
I am not able to run snackbars using my state. If I pass in open={true} property, it shows up but not working through state variable.
Replace this line
return {...};
with
return (...);
Besides that, everything seems to look good on my codesandbox.

Ag-grid custom tooltip with functional component

I am looking at ag-Grid's example on creating a custom tooltip.
import React, {Component} from 'react';
export default class CustomTooltip extends Component {
getReactContainerClasses() {
return ['custom-tooltip'];
}
render() {
const data = this.props.api.getDisplayedRowAtIndex(this.props.rowIndex).data;
return (
<div className="custom-tooltip" style={{backgroundColor: this.props.color || 'white'}}>
<p><span>{data.athlete}</span></p>
<p><span>Country: </span> {data.country}</p>
<p><span>Total: </span> {data.total}</p>
</div>
);
}
}
According to ag-Grid's react component page, "If you wish to override the style of this div you can either provide an implementation of the ag-react-container class, or via the getReactContainerStyle or getReactContainerClasses callbacks on the React component:"
How would I go about creating a custom tooltip using a functional component? I am not sure how I would provide an implementation of the getReactContainerClasses callback.
You won't be able to have the public function getReactContainerClasses in a functional component, you'd need to write a class component. If you want to write a functional component, just set the CSS class directly on the container DOM element, similarly to their vanilla JS example. Below is a functional tooltip example which sets the class custom-tooltip.
import React, {Component} from 'react';
export const FunctionalCustomTooltip = (props) => {
props.reactContainer.classList.add('custom-tooltip');
const data = props.api.getDisplayedRowAtIndex(props.rowIndex).data;
return (
<div className="custom-tooltip" style={{backgroundColor: props.color || 'white'}}>
<p><span>{data.athlete}</span></p>
<p><span>Country: </span> {data.country}</p>
<p><span>Total: </span> {data.total}</p>
</div>
);
};
Fully working example:
https://plnkr.co/edit/WHEgtw0YVia1BVP4SVO8?p=preview
You can have public function using React Hooks with useImperativeHandle hook.
export const Component = forwardRef((props: ComponentParams, ref: any) => {
useImperativeHandle(ref, () => {
return {
getReactContainerClasses() {
return ['grid-container'];
},
};
});
}

How to check whether React updates dom or not?

I wanted to check how to react does reconciliation so I updated the inner HTML of id with the same text. Ideally, it shouldn't update the dom but it is paint reflashing in chrome.
I have tried paint reflashing in chrome it is showing green rectangle over that same text
import React from 'react';
function App() {
return (
<div >
<p id="abc" key="help">abc is here</p>
<button onClick={function () {
// document.getElementById("may").innerHTML = "";
document.getElementById("abc").innerHTML = "abc is here";
}} > Btn</button>
</div>
);
}
export default App;
Expected result should be that paint reflashing shouldn't happen but it is happening.
You are not using React here to update the text of your p tag but directly updating the DOM with JavaScript.
So React reconciliation algorithm doesn't even run here.
In React, the output HTML is a result of the state and the props of your component.
When a change in state or props is detected, React runs the render method to check if it needs to update the DOM. So, in order to do this check, you need to store the parameters that determine your view in state or props.
Given your example, we could save the text you want to show in the p tag in the state of your component (using hooks):
import React, { useState } from 'react';
function App () {
const [text, setText] = useState('abc is here');
render() {
return (
<div >
<p id="abc" key="help">{this.state.text}</p>
<button onClick={() => setText('abc is here') }>Btn</button>
</div>
);
}
}
export default App;
If you are using a version of React that does not support hooks, you will need to transform your functional component into a class to use state:
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = { text: 'abc is here' };
}
render() {
return (
<div >
<p id="abc" key="help">{this.state.text}</p>
<button onClick={() => this.setState({ text: 'abc is here' }) }>Btn</button>
</div>
);
}
}
export default App;

Locate a component in React project

I wrote a Logout button component in my React app for which I wish to locate at the top right corner of the screen.
render() {
<LogoutButtonComponent height: , backgroudColor: />
}
It wouldn't let me assign any values for height and etc.
This is the Logout component:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
export default class LogOutButton extends Component {
static contextTypes = {
store: PropTypes.object.isRequired,
};
handleClick = () => {
this.props.onLogout();
};
render() {
return <button type="button" onClick={this.handleClick}>Logout</button>;
}
}
Should I locate it by < col /> ?
To add inline styles, you should defined a style object as prop, and pass it values, like doniyor2109 mentioned. There are a few caveats to using this, however.
style={{ height: 100, height: '100px', height: '100%', minHeight: '100px'}}.
Not every value should be passed as integer, some need to be passed as a string
Not every css attribute gets passed as you would expect them to, the css min-height actually gets passed as minHeight, so replace all hyphens with lower camel case style
Inline styles get insanely difficult to manage. I suggest you at the very least create an object outside the component, and pass it in like:
const DivStyle = { minHeight: '100px' }
and then:
<LogoutButtonComponent style={DivStyle} />
You can prefix that DivStyle with an export if you want to import {DivStyle} from './somefile' in other places
I suggest you check out a library like styled-components as it makes styling much easier!
I suggest you check out this article which outlines your options
You don't really add styles to your component like that. It's better to add those styles in the source for the actual component. So how exactly do you want it displayed? I will provide a template kind of and you can change it to what you want.
Go to your source for your Logout Button Component. In the return of your render method try adding a div call it container. Then add styling in a css file to that div or if you are using react-bootstrap or reactstrap or #material/ui/core you can adjust the style according to their documentation.
You can add your css for the className .container to make it appear the way you would like.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
export default class LogOutButton extends Component {
static contextTypes = {
store: PropTypes.object.isRequired,
};
handleClick = () => {
this.props.onLogout();
};
render() {
return (
<div className="container">
{* notice the className here *}
<button type="button" onClick={this.handleClick}>Logout</button>
</div>
)
}
}
Hope this helps.

List generated in constructor not calling event handler

I am generating a list on elements inside the constructor and rendering it inside render function. I am properly binding this to the event handler, but still not able to call the event handler. I have created a codepen
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
ReactDOM.render(<App boxes="9" />, document.getElementById('root'));
App.js
import React, { Component } from 'react';
import Box from './Box';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.play = this.click.bind(this);
this.boxes = [];
for (let i = 0; i < props.boxes; i++) {
this.boxes.push(<Box key={i} onClick={this.play} />);
}
}
click() {
console.log('called click');
}
render() {
return <div className="App">{this.boxes}</div>;
}
}
export default App;
However it works fine if I bind the onClick function inside the render function, or even when I call the onClick prop inside Box.js
Box.js
import React, { Component } from 'react';
import './Box.css';
class App extends Component {
render() {
return <div className="box">{this.props.display}</div>;
// return (
// <div onClick={this.props.onClick} className="box">
// {this.props.display}
// </div>
// ); -- this helps me trigger the event handler on App component
}
}
export default App;
Can you help me add the event handler without specifying the onclick handler twice i.e on both App component and Box component
Can you help me add the event handler without specifying the onclick
handler twice i.e on both App component and Box component
You have to specify it twice, once to give the function to the Box component, and second time, to let the Box component decide when to call that function; this is what you do in your second example, which is commented out.
Otherwise, how do you imagine it specifying it only once?
I suggest to strore the boxes in the state and refer the onClick within the render method, not in constructor.
You can completely get rid of constructor btw simply using arrow functions as methods.
const Box = ({onClick, label}) => (
<div onClick={onClick}>{label}</div>
)
class App extends React.Component {
state = {
boxes: [
{id: 1, label: 'Box label 1'},
{id: 2, label: 'Box label 2'},
]
}
handleClick = () => {
console.log('box click');
}
render() {
const { boxes } = this.state;
return (
<div className="App">
{boxes.map(box => (
<Box key={box.id} label={box.label} onClick={this.handleClick} />))}
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Resources