I'm currently learning ReactJS.
I created a simple application and I got two buttons.
On a desktop everything works fine and smooth.
On my iPhone using Safari however, when I click the button there is a delay before the onClick action changes appear.
I read about the 300ms delay on mobile devices but none of the solutions given worked for me. Ref: https://www.sitepoint.com/5-ways-prevent-300ms-click-delay-mobile-devices/
import React, { useState } from "react";
import './form_donation.scss';
function FormDonationAmount() {
const [amount, setAmount] = useState(0);
const [amountText, setAmountText] = useState(amount.toString());
function updateAmount(amount: number) {
if (amount > 99999) {
setAmount(99999);
} else if (amount < 0) {
setAmount(0);
} else {
setAmount(amount);
}
setAmountText(amount.toString());
}
function handleAmountChange(e: any) {
var parsed = parseInt(e.target.value);
if (isNaN(parsed)) {
setAmountText("");
updateAmount(0);
return;
}
updateAmount(parsed);
}
return (
<div className="inputs">
<div id="amount_input">
<input type="number" onChange={handleAmountChange} value={amountText} />
<button onClick={() => updateAmount(amount - 10)}>-</button>
<button onClick={() => updateAmount(amount + 10)}>+</button>
</div>
</div>
);
}
export default FormDonationAmount;
This is very frustrating for the user experience. Any ideas how I could fix this ?
Thanks
As it turns out, it was my css that was absolutely massive for the mobile to handle. So yeah, big moving blurry things in the background are cool but not very optimised...
Related
import { useState } from 'react'
import styles from '../styles/Login.module.css'
import {motion as m} from 'framer-motion'
const words = ['Sheetal','Jackie','Rohan','Narayan','Budwiser']
export default function Login() {
const [step,setStep] = useState(0)
const initial = {
transform:'translateX(100%)'
}
const animate = {
transform:'translateX(0)'
}
return (<main>
<div className={styles.carousel_parent}>
<div className={styles.track}>
{
<m.div initial={initial} animate={animate} className={styles.slides}><div>{words[step]}</div></m.div>
}
</div>
<div className={styles.controls}>
<button
disabled={step<1?true:false}
onClick={()=>{
const total = words.length;
if(step - 1 < 0){
return false
}else{
setStep(step-1)
}
}}>Prev</button>
<div className={styles.dots}>
</div>
<button
disabled={step+1>words.length-1?true:false}
onClick={()=>{
const total = words.length;
if(step + 1 > words.length-1){
return false
}else{
setStep(step+1)
}
}}>Next</button>
</div>
</div>
</main>)
}
The above is my code. What I want to acheive is when someone clicks on the next button, the slide should change with transition. I have acheived everything however the transition effect using framer motion is not working.
**What have I tried using : **
I have tried prefixing 'm.' in litrally every component but nothings seems to work.
What I am basically trying to create is a navbar that has two completely different html hierarchy based on the window size. I want it to be different for mobile than for a desktop version. eg a nav bar that is on the right on desktop and one that is on the top for mobile.
A simply state of what was doing. I created a const that would use a state of the screen size. I had used the useState() to get a default for now but I know that if I was first loading on desktop and it it was defaulted to mobile. I would have to resize first to get the desktop version instead.
const [sizeState, setSizeState] = useState("mobile");
const changeNavbar = () => {
if (window.innerWidth <= 900) {
setSizeState("mobile");
} else {
setSizeState("desktop");
}
};
window.addEventListener('resize', changeNavbar);
the sizeState would then call an if function determin what state it curently is set to.
if (sizeState === "mobile") {
return ( //some code for mobile) }
else {
// return some code for desktop
}
for now it always returns the mobile version even if loading upon a innderwidth that is above 900 abd only on resize it would do something.
I have been trying to use a onload stuff and an eventlistener that would listen to load. but i cant manage to call the changeNavbar function on the first load of the page.
I saw people recomending usein useMediaQuerry but i dont know how to get it to work based on my if (mediaquery is set to md) { return( mobile navbar) }
if someone could help me use the useMediaQuerry in this instead of my previous attempt, so that i can have two seperated returns i would also be soooooo thankful for the help!
You can simply implement it using styled-components and styled-breakpoints packages and its hooks API.
Here is an example: https://codesandbox.io/s/styled-breakpoints-n8csvf
import { down } from "styled-breakpoints";
import { useBreakpoint } from "styled-breakpoints/react-styled";
export default function App() {
const isMobile = useBreakpoint(down("sm"));
return (
<div className="App">
{isMobile ? "Mobile View" : "Desktop View"}
</div>
);
}
Or you can create custom hooks like this: https://github.com/jasonjin220/use-window-size-v2
import useWindowSize from "use-window-size-v2";
export default function App() {
const { width, height } = useWindowSize();
return (
<div className="box">
<h1>useWindowSize Hook</h1>
<p>
height: {height}
<br />
width: {width}
</p>
</div>
);
}
I'm building a portfolio app in React JS and one of my pages is an About Me page. I stumbled upon a youtube video that builds an infinite carousel using vanilla JavaScript, and during my initial testing it worked. However, when I navigate away from my 'About Me' page and return, it explodes with a "TypeError: Cannot read property 'style' of null" within my About Me component "stepNext; src/components/about-me/AboutMe.js:34".
import React from "react";
import "./AboutMe.css"
import { Button,
Fade,
Grow,
Typography } from '#material-ui/core'
import { ArrowBackIos, ArrowForwardIos } from "#material-ui/icons";
import { Background, Adventures, Hobbies } from "./about-me-components/index";
export const AboutMe = () => {
const slider = document.querySelector('.slider-about-me')
const carousel = document.querySelector('.carousel-about-me')
let direction = 1
const stepPrevious = () => {
if (direction === 1) {
slider.appendChild(slider.firstElementChild)
}
direction = -1
console.log("Previous", direction)
carousel.style.justifyContent = 'flex-end'
slider.style.transform = 'translate(33%)'
}
const stepNext = () => {
if (direction === -1) {
slider.prepend(slider.lastElementChild)
}
direction = 1
console.log("Next", direction)
carousel.style.justifyContent = 'flex-start'
slider.style.transform = 'translate(-33%)'
}
const sliderAppend = () => {
if (direction === 1) {
slider.appendChild(slider.firstElementChild)
} else if (direction === -1) {
slider.prepend(slider.lastElementChild)
}
slider.style.transition = 'none'
slider.style.transform = 'translate(0)'
setTimeout(() => {slider.style.transition = 'all 0.5s'})
}
return (
<>
<Fade
in={true}
timeout={1500}
>
<div
id='about-me-container'
>
<div className="controls">
<div
className='arrow-span-about-me arrow-left-about-me'
>
<Button
className='button-about-me arrow-about-me'
variant='contained'
onClick={stepPrevious}
>
<ArrowBackIos
className="arrow-back-about-me"
/>
</Button>
</div>
<div
className='arrow-span-about-me arrow-right-about-me'
>
<Button
className='button-about-me arrow-about-me'
variant='contained'
onClick={stepNext}
>
<ArrowForwardIos
className="arrow-forward-about-me"
/>
</Button>
</div>
</div>
<div
id="about-me-carousel-container"
>
<div
className='carousel-about-me'
>
<div
className='slider-about-me'
onTransitionEnd={sliderAppend}
>
<section className='text-white'><Background /></section>
<section className='text-white'><Adventures /></section>
<section className='text-white'><Hobbies /></section>
</div>
</div>
</div>
</div>
</Fade>
</>
)
}
The only reason I chose this route is because I haven't been able to find a half decent infinite carousel module with easy customization abilities. As much as I would prefer for this to work, I'm open to suggestions and/or solutions. Much appreciated!
I would suggest using useRef instead of document.querySelector
document.querySelector happens outside of that lifecycle, making what it returns unreliable, while refs happen within it. (Though doesn’t get reset because of a lifecycle event like a re-render.) This ensures the object returned by the ref is an accurate representation of the current state of the virtual DOM.
I think this is the reason why you are encountering the said error when you go away and back from the About Page.
Here's an example:
https://codesandbox.io/s/objective-fast-vhc27?file=/src/modalAndButton.jsx:531-648
I'm trying to use the sensor's API with react and I can't seen to be able to make it work.
It gives me an error saying that AmbientLightSensor (in my case this sensor) is undefined
If I run the script outside react ( more exactly with an extension from VSCode "live server" ) it works fine ( just a html with some JS code in it ).
That's cool and all but in this case at least I want to run this script inside react and it just doesn't let me.
So far I've tried:
running this code in react as a class method called by componentDidMount and that (Simply i've put my JS code in there ^^ )
running this code with the tag hoping that maybe react isn't really using the JS that I know and that maybe running it inside html will change this ... No it didn't do the trick
So at this point I'm unsure what to even check to make this work
Here is my code, the js code I'm trying to run is inside the Did mount component
class App extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
const details = document.getElementById("details");
// Feature detection
if (window.AmbientLightSensor) {
try {
const sensor = new AmbientLightSensor();
// Detect changes in the light
sensor.onreading = () => {
details.innerHTML = sensor.illuminance;
// Read the light levels in lux
// < 50 is dark room
if (sensor.illuminance < 50) {
document.body.className = "darkLight";
} else {
document.body.className = "brightLight";
}
};
// Has an error occured?
sensor.onerror = event =>
(document.getElementById("details").innerHTML =
event.error.message);
sensor.start();
} catch (err) {
details.innerHTML = err.message;
}
} else {
details.innerHTML =
"It looks like your browser doesnt support this feature";
}
}
render() {
return (
<div className="app">
<h1>Ambient Light Sensor</h1>
<p>Current Light Levels</p>
<div id="details"></div>
</div>
);
}
}
And also here is the working html
<head>
<meta charset="UTF-8" />
<title>Ambient Light Sensor</title>
</head>
<body class="brightLight">
<h1>Ambient Light Sensor</h1>
<p>Current Light Levels</p>
<div id="details"></div>
</body>
<script>
const details = document.getElementById("details");
// Feature detection
if (window.AmbientLightSensor) {
try {
const sensor = new AmbientLightSensor();
// Detect changes in the light
sensor.onreading = () => {
details.innerHTML = sensor.illuminance;
// Read the light levels in lux
// < 50 is dark room
if (sensor.illuminance < 50) {
document.body.className = "darkLight";
} else {
document.body.className = "brightLight";
}
};
// Has an error occured?
sensor.onerror = event =>
(document.getElementById("details").innerHTML =
event.error.message);
sensor.start();
} catch (err) {
details.innerHTML = err.message;
}
} else {
details.innerHTML =
"It looks like your browser doesnt support this feature";
}
</script>
</html>```
PS* for this to work you need to run this on a https server
That's not how React works...
I'd suggest looking into dangerouslySetInnerHTML link here: https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
And createRef link here: https://reactjs.org/docs/refs-and-the-dom.html#creating-refs
Here is a simple example utilizing both to give you a better idea:
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.detailsRef = React.createRef();
}
createMarkup() {
return { __html: 'whatever you want...' };
}
componentDidMount() {
console.log(this.detailsRef.current.innerHTML);
}
render() {
return (
<div className="app">
<h1>Ambient Light Sensor</h1>
<p>Current Light Levels</p>
<div
ref={this.detailsRef}
dangerouslySetInnerHTML={this.createMarkup()}
/>
</div>
);
}
}
export default App;
Play around with it and read the links from the official docs to adapt to your specific use case...
Never used the AmbientLightSensorAPI before but: try something like this:
class App extends Component {
constructor(props) {
super(props);
this.state = {
details: ''
};
}
componentDidMount() {
if (window.AmbientLightSensor) {
try {
const sensor = new AmbientLightSensor();
sensor.onreading = () => {
this.setState({ details: sensor.illuminance });
if (sensor.illuminance < 50) {
document.body.className = 'darkLight';
} else {
document.body.className = 'brightLight';
}
};
sensor.onerror = event =>
this.setState({ details: event.error.message });
sensor.start();
} catch (err) {
this.setState({ details: err.message });
}
} else {
this.setState({
details:
'It looks like your browser doesnt support this feature'
});
}
}
render() {
return (
<div className="app">
<h1>Ambient Light Sensor</h1>
<p>Current Light Levels</p>
<div id="details">{this.state.details}</div>
</div>
);
}
}
Ok so I got it :D
The only working way I found to send a JavaScript file and run it is by declaring it in the index.html file inside the public folder ( if u made the app with create react app )
Normally react servers you this file and it adds it's components over it.
The reason why running the script inside a react class won't do ( as far as I managed to understand ) is that react isn't actually running JavaScript there but a language based on top of JavaScript ES6.
While this means that most functionalities you may be accustomed with are working in it there are exceptions too, mostly to new functionalities ( as the sensor's API, its pretty new ).
This may not be a problem in the future, but for the time being I guess this is one way to do it.
Edit * And #SakoBu's answer turned out to be the safe way of doing
this
( #Change my mind meme :3 )
I'm attempting to do an animation with React and CSS classes. I have created a live demo, if you visit it and click the Start button you will see the text fade in and up one by one. This is the desired animation that I am after.
However, there seems to be issues of consistency when you hit Start multiple times and I cannot pinpoint why.
The Issue: Below is a recording of the issue, you can see the number 1 is not behaving as expected.
live demo
The process: Clicking Start will cancel any previous requestAnimationFrame' and will reset the state to it's initial form. It then calls the showSegments() function with a clean state that has no classNames attached to it.
This function then maps through the state adding a isActive to each segment in the state. We then render out the dom with a map and apply the new state.
This should create a smooth segmented animation as each class gets dropped one by one. However when i test this in Chrome (Version 56.0.2924.87 (64-bit)) and also on iOS, it is very inconsistent, sometimes it works perfectly, other times the first DOM element won't animate, it will just stay in up and visible it's completed transitioned state with "isActive".
I tried to replicate this issue in safari but it worked perfectly fine, I'm quite new to react so i am not sure if this is the best way to go about things, hopefully someone can offer some insight as to why this is behaving quite erratic!
/* MotionText.js */
import React, { Component } from 'react';
import shortid from 'shortid';
class MotionText extends Component {
constructor(props) {
super(props);
this.showSegments = this.showSegments.bind(this);
this.handleClickStart = this.handleClickStart.bind(this);
this.handleClickStop = this.handleClickStop.bind(this);
this.initialState = () => { return {
curIndex: 0,
textSegments: [
...'123456789123456789123456789123456789'
].map(segment => ({
segment,
id: shortid.generate(),
className: null
}))
}};
this.state = this.initialState();
}
handleClickStop() {
cancelAnimationFrame(this.rafId);
}
handleClickStart(){
cancelAnimationFrame(this.rafId);
this.setState(this.initialState(), () => {
this.rafId = requestAnimationFrame(this.showSegments);
});
}
showSegments() {
this.rafId = requestAnimationFrame(this.showSegments);
const newState = Object.assign({}, this.state);
newState.textSegments[this.state.curIndex].className = 'isActive';
this.setState(
{
...newState,
curIndex: this.state.curIndex + 1
},
() => {
if (this.state.curIndex >= this.state.textSegments.length) {
cancelAnimationFrame(this.rafId);
}
}
);
}
render(){
const innerTree = this.state.textSegments.map((obj, key) => (
<span key={obj.id} className={obj.className}>{obj.segment}</span>
));
return (
<div>
<button onClick={this.handleClickStart}>Start</button>
<button onClick={this.handleClickStop}>Stop</button>
<hr />
<div className="MotionText">{innerTree}..</div>
</div>
)
}
}
export default MotionText;
Thank you for your time, If there any questions please ask
WebpackBin Demo
Changing the method to something like this works
render(){
let d = new Date();
const innerTree = this.state.textSegments.map((obj, key) => (
<span key={d.getMilliseconds() + obj.id} className={obj.className}>{obj.segment}</span>
));
return (
<div>
<button onClick={this.handleClickStart}>Start</button>
<button onClick={this.handleClickStop}>Stop</button>
<hr />
<div className="MotionText">{innerTree}..</div>
</div>
)
}
How this helps is that, the key becomes different than previously assigned key to first span being rendered. Any way by which you can make the key different than previous will help you have this animation. Otherwise React will not render it again and hence you will never see this in animation.