getElementById after conditionally rendering with setState - javascript

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?

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"/>}

React Hook "useCourtClock" cannot be called inside a callback

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.

How to add event listener to a component and remove it when the user click anywhere else in the window?

Code:
const Component = () => {
const[divShow, setDivShow] = useState(false);
const toggleDivShow = () => {
setDivShow(!divShow)
}
return (
<div>
<button onClick={toggleDivShow}>
{
divShow && <div>click the button to show me</div>
}
</div>
)
}
now, this is working perfectly and toggle showing the div when the user click the button but, this only hide the div when the user click the button, How to hide the div when the user click anywhere else in the window
I tried to add a click event listener to the window that set divShow to false but unfortunately this didn't work, as this affected the button too and divShow always set to false even when i click the button, this is expected i think because the button is a part of the window
How can i solve this problem??
add divShow to useEffect dependency array, also dont forget to call clean up method in useEffect, as multiple document.addEventListener will cause the browser to hang.
const Component = () => {
const [divShow, setDivShow] = useState(false);
const toggleDivShow = () => {
setDivShow(!divShow);
};
useEffect(() => {
if(divShow){document.addEventListener("click", toggleDivShow)}
return () => document.removeEventListener("click", toggleDivShow); //cleaning the side effect after un-mount
}, [divShow]);
return (
<div>
<button onClick={toggleDivShow}>
{divShow && <div>click the button to show me</div>}
</button>
</div>
);
};

how can i handle event capturing in react component?

Please read code first.
After css processing, it seems like memo application's single memo paper.
The goal of the component is to print a 1 when clicked(in real, the goal is to hadding redux store's state).
When i click outside of div component, it works very well. ( it printed '1' )
but when i clicked inner div component(title, date,content), onClick event also proceed ( it printed '')
how can i prevent non-valued print?
My code :
class container extends Component {
handleState = (event) => {
console.log(event.target.id)
}
render(){
return(
<div onClick={handleState} id={value}>
<div>title</div>
<div>date</div>
<div>content</div>
</div>
)
}
}
container.defaultprops = {
value: 1
}
thanks.
You can use currentTarget:
handleState = (event) => {
console.log(event.currentTarget.id)
}
About difference between target and currentTarget:
https://stackoverflow.com/a/10086501/5709697
You can use currentTarget to check if it's the target since you bound the handler to the parent e.g.
handleState = (event) = > {
if (event.target == event.currentTarget) {
console.log(event.target.id)
}
}

setState did not work for UI change in React and Meteor

I am trying to use a button to toggle the UI display. It will show the corresponding UI when user click the button. And will hide it when user click again. I used the setState to toggle the value of "showItemBank", but the UI only show once, then went back to the invisible as default. Am I missing something important?
Here is the jsx for my App by using Meteor and React.
App = React.createClass ({
mixins: [ReactMeteorData],
getInitialState() {
return {
showItemBank: false
}
},
onItemBankClick(event) {
this.setState({
showItemBank : ! this.state.showItemBank
});
},
render() {
var classItemBank = "";
var classNewQuestion = "";
if(this.state.showItemBank === false){
classItemBank = "displayNone";
}
if(this.state.showItemBank === true){
classItemBank = "displayContent";
}
return (
<div className="container">
<header>
<form className="new-task" >
<div>
<button onClick={this.onItemBankClick} className="menu">Select From the Item Bank</button>
<button className="menu">Add New Questions</button>
</div>
<div className={classItemBank}> Hi i am from ItemBank </div>
</form>
</header>
</div>
);
}
});
try this
onItemBankClick(event) {
event.preventDefault();
this.setState({
showItemBank : ! this.state.showItemBank
});
}
buttons will try and submit the page by default
onItemBankClick(event) {
// stop the event bubbling to parent, in this case <form>
event.stopPropagation();
// prevent default browser action
event.preventDefault();
this.setState({
showItemBank : ! this.state.showItemBank
});
}

Categories