How to capture paste event anywhere on page in React? - javascript

Having looked at other stack overflow questions and on Google, I haven't been able to find an answer to this.
Background:
I'm currently re-writing a jquery application in React. Previously I was just adding a paste event listener to the body of the page. That seemed to allow me to capture any paste event the user would do.
Questions:
In React, with the code below in my App.js file, on initial load of the page it doesn't allow me to capture paste events. Only after clicking somewhere on the page does it then work when I paste. I realise I could just have an input and an onPaste attribute, but I need to be able to capture automatically.
In terms of passing the pasted text down the component tree, with the ability to edit the text from a lower component, am I right in thinking it's best to pass a callback as a prop down from the top-level component so the state is always updated using a function in the top-level component?
Currently this is what I have:
// App.js
import TopBar from "./components/TopBar/TopBar";
import Wrapper from "./components/Wrapper/Wrapper";
import AppContainer from "./components/AppContainer/AppContainer";
function App() {
const [inputText, setInputText] = useState(false);
return (
<div
onPaste={(e) => setInputText(e.clipboardData.getData("Text"))}
className="App"
>
<TopBar />
<Wrapper>
<h2>New Flashcard</h2>
<AppContainer inputText={inputText} setInputText={setInputText} />
</Wrapper>
</div>
);
}
export default App;

Your listener will only work when the specific div is focused, therefore it works on the second attempt. Instead, add the listener to body element or window object:
function Component() {
const [inputText, setInputText] = useState(false);
useEffect(() => {
window.addEventListener("paste", (e) => {
setInputText(e.clipboardData.getData("text"));
});
return () => {
window.removeEventListener("paste");
};
}, []);
return <div className="App">{inputText}</div>;
}
am I right in thinking it's best to pass a callback
No, it depends on the depth, please read about Context API and research on an anti-pattern called "props-drilling".

Related

How to pass all event down as props to a component in svelte

I'm creating some custom components in svelte and I didn't understood how to pass events as props such as on:click, on:mouseup, on:whatever, I tried in the followings way but they didn't work:
// button.svelte
// OnClick it's a prop but in this way, I need to pass every single event manually
<div on:click={OnClick} ></div>
or
// button.svelte
// I tried even in this way but it didn't really work
<div {...$$restProps} {...$$props} ></div>
The hierarchy is App => Outer => Inner
App:
<script>
import Outer from './Outer.svelte';
function handleMessage(event) {
alert(event.detail.text);
}
</script>
<Outer on:message={handleMessage}/>
Outer:
<script>
import Inner from './Inner.svelte';
</script>
<Inner on:message />
Inner:
<script>
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function sayHello() {
dispatch('message', {
text: 'Hello!'
});
}
</script>
<button on:click={sayHello}>
Click to say hello
</button>
Now, this to me is event bubbling up. If you're trying to do it in a reverse way.. I'm not so sure you can achieve that. Data is passed down, events bubble up.
When it comes to trying to send all events in one big go, there seems to be this svelte github issue created to allow on:* syntax. Sadly it isn't yet implemented.

react scroll to a component without react router and with most basic code

I have multiple sections in a web page. Need to scroll to one of the components on button click.
Router of ref is overkill or is not neccessary since it just need to scroll to a section of the page.
Tried this :
// on return function
<Button onClick={() =>
document
.getElementsByClassName("scrollhere")
.scrollIntoView() //error not a function.
}
>
Go to Download Section
</Button>
<Section1/> // Section is equivalent of div
<Section2/>
<Section3 className="scrollhere"/>
<Section4 id="downloadSection"/>
How to scroll at a certain point with most basic code?
Utilize the useRef hook to refer to an element and then scroll to it.
Something like that
const elementRef = useRef();
//Later in the code
<button onClick={() => elementRef.current.scrollIntoView()}/>
<div className="scrollhere" ref={elementRef}/>
Read more about useRef here

Building a Modal component in React without nesting it inside caller component

I'm trying to call a Modal from non-related component (without using any parent-child relationship).
In order to do that I'm trying to use React Redux (as the only way I've seen that can make a connection between two unrelated components). An example on CodeSandbox shows the bare minimum of what I'm trying to do.
My issue is that I don't want to include <Modal> inside the <Button> render function. I want to be able to simply flip the flag in Button.js and <Modal> would appear. This is, from what I understand, is suppose to be one of the advantages of Redux.
It may be look unimportant, but besides the fact that I understand that this is something that can be done and so I want to know how, it will be useful for me in a different piece of code in which if I include <Modal> in the component's render function it'll render the Modal multiple times (I render that component in a list).
Edit:
Just to be clear (as per the example on CodeSandbox), I'm using React classes and not functional components; so no hooks like useDispatch but rather functions like mapDispatchToProps are the way I want to go here.
I will Recommend using React Portal, this will inject it inside the given node, I found it to be the best solution for creating modals. I used same in dailylivedeals.com as a POC
import ReactDOM from "react-dom";
render() {
return ReactDOM.createPortal(
this.props.children,
Document.body
);
}
This is the simplest and cleanest using React's own feature.
Advantage:
Cleaner and simpler
Each modal instance can have its own modal
Multiple modals can be opened ( even from inside a modal)
Modal target can be dynamic (like modal inside modal)
Multiple modal can be controlled using code easily.
Update :
Eloborate code for modal
import React, {useEffect, useState} from "react";
import ReactDOM from "react-dom";
import {Link} from 'react-router-dom';
import "./modal.scss";
let Modal = ({visible, id, hideModal, children, ...props}) => {
let [show, setShow] = useState(false);
useEffect(() => {
setShow(visible);
console.log(visible);
}, [visible]);
let toggleVisibility = () => {
//hideModal();
setShow(!show);
}
useEffect(() => {
if (!show) {
hideModal();
}
}, [show]);
return <div className="modal-scratchpad">
{show ?
ReactDOM.createPortal(
<div id={`${id}-modal-wrapper`} className="sample-modal-wrapper">
<div id={`${id}-modal-backdrop`} className="sample-modal-backdrop">
</div>
<div id={`${id}-modal-container`} className="sample-modal-container">
<div id={`${id}-modal`} className="sample-modal">
{children}
<div onClick={toggleVisibility} className="sample-modal-cross-button">{'\u2716'}</div>
</div>
<style type="text/css">
{"body {" +
"overflow:hidden" +
"}"}
</style>
</div>
</div>
, document.body)
: <></>
}
</div>
};
export default Modal;

User UI feedback after clicking on a react-router-dom <Link/>?

Here's what I'm dealing with:
Single page app built with react + react-router-dom
User clicks on a <Link to={"/new-page-route"}/>
URL changes and my <Route/> starts rendering a new <Component/>
I mean, React is fast, but my new component takes a while to render because it's a whole new page. So it should take something between 200 and 400ms. And if I get no UI feedback, it feels that my click has not worked.
I need some kind of UI feedback so my user's know their click has been fired and something is going on. I don't think I need loaders or anything, but something to show that the click has been "accepted" by the UI.
In theory that could be handled by CSS using:
-webkit-tap-highlight-color: #SOMECOLOR
active: #SOMECOLOR
But somehow, when the URL changes and the new render begins, the browser is not being able to paint those CSS results to the screen, at least this is the behavior on Chrome and Firefox. It gets kind of weird, sometimes I see the tap-highlight and the active-change but almost always I don't see it.
NOTE: This is not the 300ms default delay on mobile that waits for the double tap. I've dealt with that using the appropriate tags.
What I thought about doing is:
Stop using the <Link/> component and use a normal <a/>.
Create a clicked state to be update after the click event
Call event.preventDefault() to prevent normal behavior of the <a/> navigation
Use the clicked state to render some new styles for the UI feedback
Fire history.push("/new-page-route") on a useEffect after clicked state has become true
Something like:
const newUrl = "/new-page-route";
const [clicked,setClicked] = useState(false);
const history = useHistory(); // HOOK FROM react-router-dom
function handleLinkClick(event) {
event.preventDefault();
setClicked(true);
}
useEffect(() => {
if (clicked === true) {
history.push(newUrl);
// OR MAYBE ADD A TIMEOUT TO BE EXTRA SURE THAT THE FEEDBACK WILL BE PAINTED
// BECAUSE I'VE SEEN THE BROWSER NOT BEING ABLE TO PAINT IF I DON'T GIVE IT SOME EXTRA TIME
setTimeout(() => history.push(newUrl),100);
}
},[clicked,history]);
// USE THE clicked STATE TO RENDER THE UI FEEDBACK (CHANGE TEXT COLOR, WHATEVER I NEED);
QUESTION
Has anyone had this issue before? What is a good way of solving this? I guess that in theory the browser should be able to paint before the new render begins, but this is not what I'm getting.
SANDBOX WITH THE ISSUE
https://codesandbox.io/s/nice-monad-4fwoc <=== CLICK HERE TO SEE THE CODE
https://4fwoc.csb.app <=== CLICK HERE TO SEE THE RESULT ONLY
On desktop: I'm able to see the BLUE background for the active state when clicking the link
On mobile: I don't see any CSS change when clicking the links. Unless I tap AND HOLD
Comments: Imagine that your component takes a while to render. Without any UI feedback, it feels that you haven't clicked at all, even though it is rendering on background.
import React from "react";
import { Link, Switch, Route } from "react-router-dom";
import styled from "styled-components";
import "./styles.css";
const HOME = "/";
const ROUTE1 = "/route1";
const ROUTE2 = "/route2";
const LS = {};
// REGULAR CSS RULES FOR THE className="link" ARE ON "./styles.css"
LS.Link_LINK = styled(Link)`
color: black;
-webkit-tap-highlight-color: red;
&:active {
background-color: blue;
}
`;
export default function App() {
return (
<React.Fragment>
<div>
<Switch>
<Route exact path={HOME} component={Home} />
<Route exact path={ROUTE1} component={Component1} />
<Route exact path={ROUTE2} component={Component2} />
</Switch>
</div>
</React.Fragment>
);
}
function Home() {
return (
<React.Fragment>
<div>I am Home</div>
<LS.Link_LINK to={ROUTE1}>Route 1 (using styled-components)</LS.Link_LINK>
<br />
<Link className={"link"} to={ROUTE1}>
Route 1 (using regular CSS)
</Link>
</React.Fragment>
);
}
function Component1() {
return (
<React.Fragment>
<div>I am Component1</div>
<LS.Link_LINK to={ROUTE2}>Route 2 (using styled-components)</LS.Link_LINK>
<br />
<Link className={"link"} to={ROUTE2}>
Route 2 (using regular CSS)
</Link>
</React.Fragment>
);
}
function Component2() {
return (
<React.Fragment>
<div>I am Component2</div>
<LS.Link_LINK to={HOME}>Home (using styled-components)</LS.Link_LINK>
<br />
<Link className={"link"} to={HOME}>
Home (using regular CSS)
</Link>
</React.Fragment>
);
}
You should use two loading phases.
Use lazy
loading
when loading the "heavy" page and display a
skeleton
component as a fallback to show the user that something is loading.
Use a component/redux state loading indicator for when your page is
loading data from apis
Trying to achive that with react-router links is not the best way because routing happends instantly, it is not the thing that stalls your application. Api calls and multiple re-renders cause an application to behave slowly.

When do React PropTypes get initialized?

I am having an issue with required PropTypes throwing errors. I would expect the PropType to throw an error if the Component was was being directly rendered. Here is a small sample of what I am trying to achieve.
You'll notice that the Button prop has a required PropType of handle click.
But I want the implementation of a Modal to be as simple as possible.
And since I don't have the context of Modal I can not bind the handleClick method directly to the Button so I pass the Button in as child and map over the children adding the handleClick method to the child component. This works pretty well besides throwing the error for Button because <Button> gets called and checked before it truly gets rendered.
I have tried to do this a few other ways as well using Higher Order Components which worked as well. But the implementation seemed convoluted and tedious this seems like a much simpler way to just generate a Modal when it is needed. You don't need any props you just pass in a child component and it will add the click handler.
It would be awesome to either Bypass the proptypes check until it is actually rendered in Modal Component or maybe there is a simpler way, all feed back is welcomed.
https://jsfiddle.net/kriscoulson/00xLw0up/1/
var Button = (props) =>
<button onClick={props.handleClick}>
{props.children}
</button>
Button.propTypes = {
handleClick: React.PropTypes.func.isRequired
}
class Modal extends React.Component {
openModal () {
console.log('Opening Modal.....')
}
childrenWithProps () {
return React.Children.map(this.props.children,(child) => React.cloneElement(child, {
handleClick: this.openModal
})
);
}
render () {
return (
<div>
{this.childrenWithProps()}
</div>
);
}
}
var App = () =>
<Modal>
<Button>Launch Modal</Button>
</Modal>
ReactDOM.render(
<App/>,
document.getElementById('container')
);
<script src="https://facebook.github.io/react/js/jsfiddle-integration-babel.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>

Categories

Resources