React Hook "useCourtClock" cannot be called inside a callback - javascript

This is my component:
const handleSubmit = useCallback(
event => {
event.preventDefault()
onDraft(id)
},
[id, onDraft]
)
return (
<form onSubmit={handleSubmit}>
<div
>
<Button type="submit" mode="strong" wide >
Click
</Button>
</div>
</form>
)
I need to now add something when handleSubmit gets called. Above the onDraft function, I want to add const { test } = useClock() , but it says:
React Hook "useCourtClock" cannot be called inside a callback.
How do I fix this ? Mainly, I need useClock to be called when user clicks on the button, because I want to know if anything has changed before he clicks on the button.

Related

Changing style of another element using react

I'm relatively new to react and am totally lost trying to figure out how to make an Component appear when I press on a button. I've set up the code as such
<Button>GO</Button>
<CalendarIcon id="calendar visibility="hidden"/>
and then useVisibility()
export default function useVisibility() {
const[visibility, setVisibility] = useState("hidden")
useEffect(() => {
function handleVis(){
setVisibility("visible")
}
button.addEventListener("onClick", handleVis)
return () => button.removeEventListener("onClick", handleVis)
}, [])
return visibility
}
My problem is that I don't know how to pass the button into the function so that I can add the event listener. If I am doing this in a totally roundabout way or overcomplicating it please tell me because I am so lost.
Thanks!
What I would do is let each instance where you render a button specify how its click handler should behave as there can be many use cases for a button in a website.
function MyComponent() {
const[isVisible, setIsVisible] = useState(false)
const handleVisibilityToggle = useCallback(
() => setIsVisible(!isVisible),
[isVisible, setIsVisible]
)
...
const visibility = isVisible ? 'unset' : 'hidden'
return (
<>
...
<Button onClick={handleVisibilityToggle}>GO</Button>
<CalendarIcon id="calendar" visibility={visibility}/>
</>
)
}
if you would like to clean up how that code is used and abstract the logic to a visibility hook it would look something like this
function useVisibilityToggle(defaultValue = false) {
const[isVisible, setIsVisible] = useState(defaultValue)
const toggleVisibility = useCallback(
() => setIsVisible(!isVisible),
[isVisible, setIsVisible]
)
const visibility = isVisible ? 'visible' : 'hidden'
return [visibility, toggleVisibility]
}
function MyComponent() {
const [visibility, toggleVisibility] = useVisibilityToggle(false)
return (
<>
...
<Button onClick={toggleVisibility}>GO</Button>
<CalendarIcon id="calendar" visibility={visibility}/>
</>
)
}
Check the first example here: https://reactjs.org/docs/hooks-state.html
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
That should show you how to manipulate the state when the button is clicked. Next you wanted to show/hide something when that button is clicked.
First let's change that useState to a boolean one, so
const [hidden, setHidden] = useState(false);
Then we can change the button so that it uses the previous state to set the new one. You should never use the hidden in the setHidden, but instead pass a function into it, which gets the previous value as parameter.
<button onClick={() => setHidden(prevHidden => !prevHidden)}>
And now we want to use that value, so we can add something below the button:
{ !hidden ? (<p>This text is visible</p>) : (<></>) }
Working example: https://codesandbox.io/s/strange-williamson-wuhnb?file=/src/App.js
Your code looks like you are trying to build a custom hook, that's more advanced stuff, and if you are super new to React, you won't need that right now.
Your main goal is to show CalendarIcon component visible when you click on GO Button.
So you need a state lets say visible variable to control this.
You can update this state onClick of your Button as shown below to true or false, And make visibility="visible" always.
When visible will be true your CalendarIcon will appear.
const [visible, toggleVisibility] = useState(false)
<Button onClick={()=> toggleVisibility(!visible)}>GO</Button>
{visible && <CalendarIcon id="calendar" visibility="visible"/>}

How can I get focused element and change state in React component?

My intention is to update the 'isEditorFocused' state whenever the focused element changed, and if the div contains the focused element, deliver true into the Editor component.
However, the code does not work as my intention... It updates state only the first two times.
This is my Code. Actually not the exact code, but it is the core part of my question. If there is any typo, please ignore it. I checked it all in my real code file.
export default AddArticle = () => {
const [isEditorFocused, setIsEditorFocused] = React.useState(false);
const editorRef = React.useRef(null);
React.useEffect(() => {
if(editorRef.current !== null) {
if(editorRef.current.contains(document.activeElement)
setIsEditorFocused(true);
else
setIsEditorFocused(false);
}
}, [document.activeElement]}
return (
<div ref={editorRef} tabIndex="0">
<Editor isEditorFocused={isEditorFocused}></Editor>
<FileUploader {some props}/>
</div>
)
}
Not to completely change your code, but couldn't you just use onFocus and onBlur handlers?
For example:
const AddArticle = () => {
const [isEditorFocused, setIsEditorFocused] = React.useState(false);
return (
<div
onFocus={() => {
setIsEditorFocused(true);
}}
onBlur={() => {
setIsEditorFocused(false);
}}
tabIndex="0"
>
<Editor isEditorFocused={isEditorFocused}></Editor>
</div>
);
};
Working codepen
As T J mentions so eloquently, your issue is with document.activeElement
Note regarding React's current support for onFocus vs onFocusIn:
React uses onFocus and onBlur instead of onFocusIn and onFocusOut. All React events are normalized to bubble, so onFocusIn and onFocusOut are not needed/supported by React.
Source: React Github
The main problem is this: [document.activeElement].
The useEffect dependency array only works with React state, and document.activeElement is not React state.
You can try using a focusin event listener on your <div>, if it receives the event it means itself or something inside it got focus, since focusin event bubbles as long as nothing inside is explicitly preventing propagation of this event.
try this way.
const AddArticle = () => {
const [isEditorFocused, setIsEditorFocused] = React.useState(false);
const handleBlur = (e) => {
setIsEditorFocused(false)
};
handleFocus = (){
const currentTarget = e.currentTarget;
if (!currentTarget.contains(document.activeElement)) {
setIsEditorFocused(true);
}
}
return (
<div onBlur={handleBlur} onFocus={handleFocus}>
<Editor isEditorFocused={isEditorFocused}></Editor>
</div>
);
};

Hold onClick of input type=file, do something I want, and then let browsers start a file-dialog?

I want to display a custom warning message when <input type='file'...> is clicked, and then let a browser starts a file-selection dialog with clicking 'Confirm' button. This is to make sure users aware of the warning message correctly. Is it possible?
import React from "react";
import "./styles.css";
export default function App() {
const [warn, setWarn] = React.useState(false);
const inputRef = React.useRef();
const handleChange = e => {};
const handleClick = e => {
if (!warn) {
setWarn(true);
e.preventDefault();
}
};
const handleContinue = e => {
const event = new Event("input", { bubbles: true });
inputRef.current.dispatchEvent(event);
};
return (
<div className="App">
{warn ? (
<div>
<h1>WARNING MESSAGE...</h1>
<button onClick={handleContinue}>Confirm</button>
</div>
) : (
""
)}
<label htmlFor="test-input">
<input
id="test-input"
ref={inputRef}
type="file"
onChange={handleChange}
onClick={handleClick}
/>
</label>
</div>
);
}
When input is clicked, check if click event was triggered by the browser or via a script. Event.isTrusted will be true when click event is triggered by the browser, so in this case use Event.preventDefault() to prevent file dialog from opening.
Then write the code that does what you want to do and finally call event.target.click() to trigger the click event again but this time Event.isTrusted will be false, so file dialog will not be prevented from opening.
Keep in mind that code you want to execute, before the file dialog is opened, should be in the else block. Otherwise, it will execute two times.
const input = document.querySelector('input');
input.addEventListener('click', (e) => {
if (e.isTrusted) {
e.preventDefault();
} else {
console.log('doing something....');
console.log('done. Opening file dialog');
}
e.target.click();
})
<input type="file"/>
You can trigger the file picker with input.click() but that will also change event.isTrusted to false since it wasn't triggered manually by a user interaction
picker.onclick = evt => {
evt.isTrusted && evt.preventDefault(confirm('are you sure?') && evt.target.click())
}
<input id="picker" type="file">
`

getElementById after conditionally rendering with setState

I have a trigger button that will open a dialog asking if a user would like to enable text to speech. Once the dialog is open, I want to focus on the yes button within the dialog by getting the button element by its ID.
When the trigger is pressed, the following function is called:
private openTTSDialog = () => {
if (this.state.ttsDialog === true) {
this.setState({ ttsDialog: false })
} else {
this.setState({ ttsDialog: true }, () => {
// search document once setState is finished
const yesButton = document.getElementById('tts-dialog-yes-button')
log('yesButton', yesButton)
if (yesButton) {
yesButton.focus()
}
})
}
}
And my dialog is conditionally rendered with a ternary expression like this:
{
this.state.ttsDialog ? (
<div className="tts-dialog-container">
<div className="tts-dialog-text-container">
{session.ttsEnabled ? (
<div>
{
strings.disableTTS
}
</div>
) : (
<div>
{
strings.enableTTS
}
</div>
)}
</div>
<div className="tts-dialog-button-container">
<button
aria-label={strings.yes}
tabIndex={0}
className="tts-dialog-button"
id="tts-dialog-yes-button" // this is the button I want to focus
onClick={this.toggleTTS}
>
{
strings.yes
}
</button>
<button
aria-label={strings.no}
tabIndex={0}
className="tts-dialog-cancelButton"
onClick={this.closeTTSDialog}
>
{
strings.no
}
</button>
</div>
</div>
) : null
}
My log for yesButton is undefined. I thought adding the callback function to setState would fix this because I would be searching the document after setState was finished, but I'm still missing something. Any idea what it is?
In the constructor of your class, you should add a ref to your button:
this.myRef = React.createRef();
Then in your button :
<button
ref={this.myRef}
aria-label={strings.yes}
tabIndex={0}
className="tts-dialog-button"
id="tts-dialog-yes-button" // this is the button I want to focus
onClick={this.toggleTTS}
>
Finally, instead of doing:
const yesButton = document.getElementById('tts-dialog-yes-button')
You should do :
const yesButton = = this.myRef.current;
Actually I would also think this should work since you use a callback on setState, so the new render should have completed and the element should already be mounted and accessible. Anyway I think the idiomatic React way for this would be to use a ref (https://reactjs.org/docs/refs-and-the-dom.html) and put it on the button like <button ref={this.yesButton} ...>...</button> and then call this.yesButton.focus(). Have you tried that already?

ReactJS OnKeyPress to trigger a button press

I'm very new to ReactJS and I'm just trying to do some small things to understand more.
I was wondering if the OnKeyPress can trigger a button press. I've seen a few similar questions but what the OnKeyPress triggered was just a console.log or an alert. So I wasn't sure how to trigger the button press.
This is what I have so far:
class Form extends React.Component {
onButtonPress = (e) => {
// this is just an example of what happens when the button is pressed.
this.setState({isClicked: true});
}
keyPress = (event) => {
if (event.key == 'Enter'){
// How would I trigger the button that is in the render? I have this so far.
this.onButtonPress();
}
}
render() {
return (
<div>
<div className="fieldForm">
<input
value={name}
type="name"
onKeyPress={this.keyPress}
/>
</div>
<Button onClick={this.onButtonPress}>Submit</Button>
</div>
)
}
}
Please note that I didn't include everything in here such as the constructor, props, or the state object attributes.
The purpose of this is to make it look like the button has been clicked. When the button is clicked, it'll show a small loading sign on the button. I want the same thing to happen if I were to press enter (with the loading sign on the button, that's why I want the button pressed).
Is this possible?
Programmatically triggering DOM events is not something you should do unless you have very specific needs.
Both onKeyPress and onClick are event handlers, you can do anything you want when an event happens. I would just call a function that sets the state you want from both handlers.
Here's an example:
class Form extends React.Component {
handleFormSubmit = () => {
this.setState({ isClicked: true });
};
handleButtonPress = () => {
this.handleFormSubmit();
};
handleKeyPress = event => {
if (event.key == 'Enter') {
this.handleFormSubmit();
}
};
render() {
return (
<div>
<div className="fieldForm">
<input value={name} type="name" onKeyPress={this.handleKeyPress} />
</div>
<Button onClick={this.handleButtonPress} loading={this.state.Load}>
Submit
</Button>
</div>
);
}
}
In case you have no other way and you should click on this element for some vague reason and the method that elas said didn't work for you. try this:
onButtonPress = (e) => {
console.log('hi hi')
}
handleKeyPress = (event) => {
if (event.key === 'Enter') {
this.refs.but.click()
}
}
render () {
return (
<Layout>
<div>
<div className="fieldForm">
<input
value={'name'}
type="name"
onKeyPress={(e) => this.handleKeyPress(e)}
/>
</div>
<Button onClick={this.onButtonPress} ref="but">Submit</Button>
</div>
</Layout>
)
}

Categories