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>
);
};
Related
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"/>}
folks!
Does anyone know the opposite method to cellFocused in ag-grid?
I need to detect when the focused cell loses its focus and run some actions.
Thanks for your responses.
I've found a way to support onBlur event. Since ag-grid doesn't have a built-in method, I created wy own event listener to the focus cell node and remove it after losing the focus state.
So, my code looks like this. Inside the react class I have 3 additional methods:
removeCellBlurListener = () => {
const target = document.activeElement;
if (target) {
target.removeEventListener('blur', this.onCellBlur);
}
};
addCellBlurListener = () => {
const target = document.activeElement;
if (target) {
target.addEventListener('blur', this.onCellBlur);
}
};
onCellBlur = () => {
...do something on blur
};
render () {
return (
<AgGridReact
{...restProps}
onCellFocused={(e) => this.addCellBlurListener()}
onGridReady={this.onGridReady}
/>
);
}
I am creating an input that adds to a list of tags rendered below the input. I would like the input to push the tag to the list when 'Enter' is pushed while the user is focused on the input field. The field then resets to blank or "", and the user can add another tag in the same fashion.
The problem I am having is that after the value gets reset to a blank string, any time I try to grab the value of the input an empty string is returned.
Here is what I have so far:
const [listOfOptions, addOption] = React.useState([])
let newOption = () =>{
let addField = document.getElementById('addOption')
let grabText = addField.value;
console.log('Grabtext ='+ grabText)
addOption([...listOfOptions, grabText])
addField.reset();
}
useEffect(() => {
document.getElementById('addOption').addEventListener("keyup", function(event) {
if (event.key === "Enter") {
newOption()
}}
); }, [listOfOptions])
I would avoid directly manipulating the DOM in React. Let React's Virtual DOM handle that.
You can store the value of the form in state and then on submit it resets the state to ''.
Something like this:
const [value, setValue] = useState('');
const handleFormValueChange = (event) => setValue(event.target.value);
const resetFormValue = () => setValue('');
<input type="text" value={value} onChange={(event) => handleFormValueChange(event)} />
<input type="submit" onSubmit={resetFormValue} />
The answer, taking into consideration the earlier suggestions, thanks for the help, would then be the below:
const [value, setValue] = useState('');
const [listOfOptions, addOption] = React.useState([])
const handleFormValueChange = (event) => setValue(event.target.value);
let newOption = (event) => {
if (event.key === "Enter") {
addOption([...listOfOptions, value]);
setValue('');
}
}
<input value={value} onChange={(event) => handleFormValueChange(event)} onKeyUp={(event)=>newOption(event)} id="addOption" />
This solution uses the Enter keydown to submit and reset the value, whilst modifying only React's virtual DOM.
Assuming that addOption is an <input>, <select>, or <textarea>, it should not have reset() method. Modify .value instead
addField.value = "";
Don't forget to detach eventListener on this component dismount.
useEffect(() => {
const element = document.getElementById("addOption");
const listener = event => {
if (event.key === "Enter") {
newOption();
event.preventDefault();
}
};
element.addEventListener("keyup", listener);
return () => element.removeEventListener("keyup", listener);
}, [listOfOptions]);
Also please avoid directly modifying DOM directly to a React rendered element, as it may disrupt React render process.
Instead, use a state library such as this or event library
Edit:
You should avoid manipulating DOM directly as told in this answer by David Caldwell
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)
}
}
I could explain what I am trying to do, but this ReactJS example is a walkthrough of exactly what I want. The problem is I can't figure out what the equivelant would be for react native.
Basically, when I press return in the TextInput, I want the text cleared and focus maintained.
Any thoughts?
I've submitted a PR with a blurOnSubmit property.
Set it to false and the TextInput never blurs, onSubmitEditing still fires though.
Hopefully it gets merged. :)
https://github.com/facebook/react-native/pull/2149
I came out with following (working) solution:
var NameInput = React.createClass({
getInitialState() {
return {
textValue: ''
}
},
clearAndRetainFocus: function(evt, elem) {
this.setState({textValue: elem.text});
setTimeout(function() {
this.setState({textValue: this.getInitialState().textValue});
this.refs.Name.focus();
}.bind(this), 0);
},
render() {
return(
<TextInput
ref='Name'
value={this.state.textValue}
onEndEditing={this.clearAndRetainFocus} />
)
}
});
So, basically when we end editing, we will set the textValue state to the value of the TextInput and right after that (in setTimeout), we switch it back to default (empty) and retain focus on the element.
I don't know how to trigger blurOnSubmit but if you do and it works you should do that. Another thing I found that works with a functional react component in a chat application i am making is this:
... import statments
const ChatInput = props => {
const textIn = React.useRef(null) //declare ref
useEffect(()=>textIn.current.focus()) //use effect to focus after it is updated
const textInputChanged = (text) =>{
props.contentChanged(text);
}
const submitChat = () =>{
const txt = props.content.trim()
txt.length >0 ? props.sendChat(txt, props.username) : null;
}
const keyPressEvent = (e) =>{
return e.key == 'Enter'? submitChat() : null;
}
return (
<TextInput
style={styles.textInput}
keyboardType={props.keyboardType}
autoCapitalize={props.autoCapitalize}
autoCorrect={props.autoCorrect}
secureTextEntry={props.secureTextEntry}
value={props.content}
onChangeText={textInputChanged}
onKeyPress={keyPressEvent}
autoFocus={true} //i don't think you need this since we are using useEffect
ref={textIn} //make it so this is the ref
/>
)}
... export default react-redux connect stuff
if you have more inputs you can probably do some sort of ref choosing logic in the useEffect hook
this is the article that helped me figure it out, it's almost the same thing:
https://howtocreateapps.com/how-to-set-focus-on-an-input-element-in-react-using-hooks/