So, I don't understand how 'self' is never defined - reactjs

How to implement auto onClick button event in reactjs after some interval?

The button will be clicked every 10 seconds, you can use ref to click the button wherever you want:
import React, { useEffect, useRef } from "react";
function App() {
const button = useRef();
const interval = useRef();
useEffect(() => {
interval.current = setInterval(() => {
button.current.click();
}, 10000);
return () => {
clearInterval(interval.current);
}
}, []);
return (
<div>
<button
type="button"
ref={button}
onClick={() => { console.log('button clicked') }}
>
AutoClickable
</button>
</div>
);
}

It should be clicked every 5 second. If you need it once use setTimeout instead
return (
<button onClick={setInterval(() => {return null}, 5000)}>Autoclickable</button>
)
OR
const handleClick = () => {
console.log('Auto click')
}
React.useEffect(() => {
const interval = setInterval(() => {
handleClick()
}, 5000)
})
return (
<button onClick={handleClick}>Autoclickable</button>
)

Related

Add a Spinner while waiting for promise to resolve in react

const fetchMusic= () => {
return new Promise((resolve) =>
setTimeout(() => {
const music = musicList.sort(() => 0.5 - Math.random()).slice(0, 4);
resolve({ data: music});
}, 300)
);
};
export default fetchMusic;
const getRandomMusic = () => {
return fetchMusic().then((result) => result.data);
};
const Button = (props) => {
return (
<div>
<Button {...props} onClick={getRandomMusic.bind(this)} />
<SomeComponent />
<p>Some text here</p>
</div>
);
};
I want add a spinner while waiting for the promise to resolve .
fetchMusic is in some other file.I m importing it in a component .
TLDR
How about use useState and useCallback for that action
Answer
At terms of react, use State for loading action is right use case.
So, When to start function, use can setLoading(true) and after action you can setLoading(false) for make loading effect
const fetchMusic= () => {
return new Promise((resolve) =>
setTimeout(() => {
const music = musicList.sort(() => 0.5 - Math.random()).slice(0, 4);
resolve({ data: music});
}, 300)
);
};
export default fetchMusic;
const Button = (props) => {
const [loaidng, setLoading] = useState(false);
const getRandomMusic = useCallback(() => {
setLoading(true)
return fetchMusic().then((result) => {
setLoading(false);
result.data
});
},[]);
return (
<div>
<Button {...props} onClick={getRandomMusic.bind(this)} />
{loading && <Sipinner/>}
<SomeComponent />
<p>Some text here</p>
</div>
);
};
Reference
Example of loading
ETC
If you have any other question. Just give me comment please.

When accessing a state variable from a useCallback, value is not updated

At a certain place of my code I'm accessing a state variable of my component from a call back ( UserCallback ) and I find the state variable has not updated from the initial value and call back is referring to the initial value. As I read in the documentation when variable is passed as one of array items then it should update the function when it is updated. Following is a sample code.
const Child = forwardRef((props, ref) => {
const [count, setCount] = useState(0);
const node = useRef(null);
useImperativeHandle(ref, () => ({
increment() {
setCount(count + 1);
}
}));
const clickListener = useCallback(
e => {
if (!node.current.contains(e.target)) {
alert(count);
}
},
[count]
);
useEffect(() => {
// Attach the listeners on component mount.
document.addEventListener("click", clickListener);
// Detach the listeners on component unmount.
return () => {
document.removeEventListener("click", clickListener);
};
}, []);
return (
<div
ref={node}
style={{ width: "500px", height: "100px", backgroundColor: "yellow" }}
>
<h1>Hi {count}</h1>
</div>
);
});
const Parent = () => {
const childRef = useRef();
return (
<div>
<Child ref={childRef} />
<button onClick={() => childRef.current.increment()}>Click</button>
</div>
);
};
export default function App() {
return (
<div className="App">
<Parent />
</div>
);
}
What I'm originally building is a custom confirmation modal. I have a state variable which set either display:block or display:none to the root element. Then if there is a click outside the component I need to close the modal by setting state variable to false. Following is the original function.
const clickListener = useCallback(
(e: MouseEvent) => {
console.log('isVisible - ', isVisible, ' count - ', count, ' !node.current.contains(e.target) - ', !node.current.contains(e.target))
if (isVisible && !node.current.contains(e.target)) {
setIsVisible(false)
}
},
[node.current, isVisible],
)
It doesn't get closed because isVisible is always false which is the initial value.
What am I doing wrong here?
For further clarifications following is the full component.
const ConfirmActionModal = (props, ref) => {
const [isVisible, setIsVisible] = useState(false)
const [count, setCount] = useState(0)
const showModal = () => {
setIsVisible(true)
setCount(1)
}
useImperativeHandle(ref, () => {
return {
showModal: showModal
}
});
const node = useRef(null)
const stateRef = useRef(isVisible);
const escapeListener = useCallback((e: KeyboardEvent) => {
if (e.key === 'Escape') {
setIsVisible(false)
}
}, [])
useEffect(() => {
stateRef.current = isVisible;
}, [isVisible]);
useEffect(() => {
const clickListener = e => {
if (stateRef.current && !node.current.contains(e.target)) {
setIsVisible(false)
}
};
// Attach the listeners on component mount.
document.addEventListener('click', clickListener)
document.addEventListener('keyup', escapeListener)
// Detach the listeners on component unmount.
return () => {
document.removeEventListener('click', clickListener)
document.removeEventListener('keyup', escapeListener)
}
}, [])
return (
<div ref={node}>
<ConfirmPanel style={{ display : isVisible ? 'block': 'none'}}>
<ConfirmMessage>
Complete - {isVisible.toString()} - {count}
</ConfirmMessage>
<PrimaryButton
type="submit"
style={{
backgroundColor: "#00aa10",
color: "white",
marginRight: "10px",
margin: "auto"
}}
onClick={() => {console.log(isVisible); setCount(2)}}
>Confirm</PrimaryButton>
</ConfirmPanel>
</div>
)
}
export default forwardRef(ConfirmActionModal)
You assign a function clickListener to document.addEventListener on component mount, this function has a closure on count value.
On the next render, the count value will be stale.
One way to solve it is implementing a function with refernce closure instead:
const Child = forwardRef((props, ref) => {
const [count, setCount] = useState(0);
const countRef = useRef(count);
useEffect(() => {
countRef.current = count;
}, [count]);
useEffect(() => {
// countRef.current always holds the most updated state
const clickListener = e => {
if (!node.current.contains(e.target)) {
alert(countRef.current);
}
};
document.addEventListener("click", clickListener);
return () => {
document.removeEventListener("click", clickListener);
};
}, []);
...
}
You can pass a callback to setIsvisible so you don't need isVisible as a dependency of the useCallback. Adding node.current is pointless since node is a ref and gets mutated:
const clickListener = useCallback((e) => {
setIsVisible((isVisible) => {//pass callback to state setter
if (isVisible && !node.current.contains(e.target)) {
return false;
}
return isVisible;
});
}, []);//no dependencies needed
While your clickListener does change when count changes you only bind the initial clickListener once on mount because your useEffect dependency list is empty. You could ad clickListener to the dependency list as well:
useEffect(() => {
// Attach the listeners on component mount.
document.addEventListener("click", clickListener);
// Detach the listeners on component unmount.
return () => {
document.removeEventListener("click", clickListener);
};
}, [clickListener]);
Side note: using node.current in a dependency list doesn't do anything as react does not notice any changes to a ref. Dependencies can only be state or props.

How do you prevent the component from disappearing too soon?

const FlashMessage = (props) => {
const [isOpen, setIsOpen] = useState(true);
const hideComponent = () => {
setisOpen(false);
};
useEffect(() => {
setIsOpen(true);
setTimeout(() => hideComponent(), 9000);
}, [props]);
return (
(props.flashMessage === true && isOpen) ?
<View style={styles.main}>
<Text style={styles.message}>{props.message}</Text>
</View>
: null
);
}
I have this Flash Message component in my React Native app, and sometimes, the Flash Message disappears after 2 second. It seems to appear on a random basis and it's probably due to a problem with useEffect and setTimeout, but I have not been able to figure out what might be causing this.
The effect you have with [props] as dependency doesn't make sense to me.
But you can have an isolated effect for the isOpen boolean.
useEffect(() => {
setTimeout(() => {
setIsOpen(false);
}, 9000);
}, [isOpen]);
Here is a full working example, simplified:
export default function App() {
const [show, setShow] = useState(false);
useEffect(() => {
setTimeout(() => {
setShow(false);
}, 2000);
}, [show]);
return (
<div className="App">
<button onClick={e => setShow(true)}>Show</button>
{show && <div>hello</div>}
</div>
);
}

OutSider click event using React Hook

I am trying to develop a click event handler function for the DOM element so that when I click to the outside of the div the corresponding dom element close. I have been trying the following code but I am getting the error of TypeError: node.contains is not a function. Not sure if I am doing it correctly with the react hook. Any kinds of help would be really appreciated.
import React, { useState, useEffect, useRef } from 'react';
const OutSiderClickComponent = () => {
const [visible, setVisible] = useState(false);
const node = useRef();
const handleClick = () => {
if (!visible) {
document.addEventListener('click', handleOutsideClick, false);
} else {
document.removeEventListener('click', handleOutsideClick, false);
}
setVisible(prevState => ({
visible: !prevState.visible,
}));
}
const handleOutsideClick = (e) => {
if (node.contains(e.target)) {
return;
}
handleClick();
}
return(
<div ref={node}>
<button onClick={handleClick}>Click to See</button>
{visible && <div>You Clicked the Button</div>}
</div>
);
};
export default OutSiderClickComponent;
When you use useRef you need to remember that the value is in current attribute of ref.
Try node.current.contains().
The rest should look more like that, using React.useEffect:
const handleOutsideClick = (e) => {
if (node.current.contains(e.target)) {
console.log('clicked inside');
// this.setVisible(true);
} else {
this.setVisible(false);
}
}
React.useEffect(() => {
document.addEventListener('click', handleOutsideClick, false);
return () => void document.removeEventListener('click', handleOutsideClick, false);
}, []);
and
<button onClick={() => void setVisible(true)}>Click to See</button>
There are two changes. First, you need to use node.current to check for the ref,
node.current.contains(e.target) . Also the ref must be attached to the node to which you need to detect outside click
var { useState, useEffect, useRef } = React;
const OutSiderClickComponent = () => {
const [visible, setVisible] = useState(false);
const node = useRef();
const handleClick = () => {
if (!visible) {
document.addEventListener('click', handleOutsideClick, false);
} else {
document.removeEventListener('click', handleOutsideClick, false);
}
setVisible(prevState => ({
visible: !prevState.visible,
}));
}
const handleOutsideClick = (e) => {
if (node.current.contains(e.target)) {
return;
}
setVisible(prev => !prev.visible)
}
return(
<div>
<button onClick={handleClick}>Click to See</button>
{visible && <div ref={node}>You Clicked the Button</div>}
</div>
);
};
ReactDOM.render(<OutSiderClickComponent />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="app" />

Strange React hooks behavior, can't access new state from a function

I use the library react-use-modal, and
I'm trying to read the updated value of confirmLoading when inside the handleClick function.
handleClick does read the first value of confirmLoading defined when doing const [ confirmLoading, setConfirmLoading ] = useState(false), but never updates when I setConfirmLoading inside handleOk.
I don't understand what I'm doing wrong
import { Button, Modal as ModalAntd } from 'antd'
import { useModal } from 'react-use-modal'
export interface ModalFormProps {
form: React.ReactElement
}
export const ModalForm: React.FC = () => {
const [ confirmLoading, setConfirmLoading ] = useState(false)
const { showModal, closeModal } = useModal()
const handleOk = () => {
setConfirmLoading(true)
setTimeout(() => {
setConfirmLoading(false)
closeModal()
}, 1000)
}
const handleCancel = () => {
closeModal()
}
const handleClick = () => {
showModal(({ show }) => (
<ModalAntd
onCancel={handleCancel}
onOk={handleOk}
title='Title'
visible={show}
>
// the value of confirmLoading is always the one defined
// with useState at the beginning of the file.
<p>{confirmLoading ? 'yes' : 'no'}</p>
</ModalAntd>
))
}
return (
<div>
<Button onClick={handleClick}>
Open Modal
</Button>
</div>
)
}
This is happening because of closures. The component that you pass to showModal remembers confirmLoading and when you call function setConfirmLoading your component renders again and function handleClick is recreated. 'Old' handleClick and 'old' component in showModal know nothing about the new value in confirmLoading.
Try to do this:
export const ModalForm: React.FC = () => {
const { showModal, closeModal } = useModal();
const handleClick = () => {
showModal(({ show }) => {
const [ confirmLoading, setConfirmLoading ] = useState(false);
const handleOk = () => {
setConfirmLoading(true)
setTimeout(() => {
setConfirmLoading(false)
closeModal()
}, 1000)
};
const handleCancel = () => {
closeModal()
};
return (
<ModalAntd
onCancel={handleCancel}
onOk={handleOk}
title='Title'
visible={show}
>
// the value of confirmLoading is always the one defined
// with useState at the beginning of the file.
<p>{confirmLoading ? 'yes' : 'no'}</p>
</ModalAntd>
);
})
};
return (
<div>
<Button onClick={handleClick}>
Open Modal
</Button>
</div>
)
}

Resources