I have a parent component which can get focus. It uses this focus to offer keyboard controls. This parent component can spawn a child component which similarly can take focus so that it can respond to keyboard events. One of the keyboard events that the child component listens for is <esc> which causes the child component to get unmounted.
When the child component unmounts, focus is returned to the <body> of the document.
How can my parent component detect when this happens and reassign focus to itself?
What I know so far:
React's synthetic onBlur handler does get bubbled blur events from it's children (unlike non-synthetic events). But, blur events are not triggered when the element with focus leaves the DOM.
React does not implement a onFocusOut listener, but if I register one directly using a ref, I do get an event telling me that the child has unmounted. However, I can't distiguish between a focusout event triggered by a child unmounting, and a focusout event triggered by a user clicking into a different click target.
Edit: I’m looking for a solution that doesn’t involve direct communication/coupling between the parent and child components. Imagine that there might me arbitrarily many children like this in an arbitrarily deeply nested tree.
I ended up solving this using a MutationObserver.
The code looks like this:
// It's possible for a child component to gain focus and then become
// unmounted. In that case, the browser will return focus to the `<body>`.
// In the following hook, use a `MutationObserver` to watch for that behavior
// and refocus the containing FocusTarget when it happens.
//
// I tried a number of other approaches using `focus/blur/focusin/focusout` on
// various DOM nodes, and was unable to find a solution which would trigger in
// this senario in Firefox. Therefore we use this `MutationObserver` approach.
useEffect(() => {
// Only create the `MutationObserver` within the currently focused target.
if (ref == null || windowId !== focusedWindowId) {
return;
}
const observer = new MutationObserver(mutations => {
// In the common case we won't have focused the body, so we can do this
// inexpensive check first to avoid calling the more expensive `O(n)`
// check of the individual mutations.
if (document.activeElement !== document.body) {
return;
}
if (mutations.some(mutation => mutation.removedNodes.length > 0)) {
ref.focus();
}
});
observer.observe(ref, {
subtree: true,
attributes: false,
childList: true
});
return () => observer.disconnect();
}, [windowId, focusedWindowId, ref]);
Actual commit adding it: https://github.com/captbaritone/webamp/commit/2dca07ff0a97ad378a1a050513255d2ba129dbcd
The child component should accept a function prop that gets called when esc is pressed (or wherever else it needs to be called).
// Render function of Parent
const functionThatGetsCalledWhenEscIsPressed = function(){
this.input.focus();
}
return (
<div>
<Child onDestroy={functionThatGetsCalledWhenEscIsPressed} />
<input ref={(input) => this.input = input} />
</div>
);
You can pass a callback from the parent to the child to be called in the case that the <esc> key is pressed (or just in the componentWillUnmount method of the child). This callback can just transfer focus to the parent, in the same manner that happens usually.
For example:
class Parent extends React.Component {
focusSelf() {
// do stuff to focus self
}
render() {
return (
<div>
<Child beforeUnmount={ this.focusSelf }/>
</div>
)
}
}
class Child extends React.Component {
componentWillUnmount() {
const { beforeUnmount } = this.props;
beforeUnmount();
}
}
Related
If I use setState in the child and place a callback in the parent to update the parent state that propagates to child props, then I end up with two render calls.
One for the update to the child state, and one for the prop changing. I can manually use shouldComponentUpdate to ignore the prop change if I want, but the render won't be ready until the state updates.
I know all this can be done easily in react 16-18, but migrating is not simple at the moment.
I am wondering if the solution is to make the child state the source of truth. I can solve all my problems this way, but I thought in react you typically made the parent the source of truth.
Parent Component
Child Component
ChildComponent
function = () => {
this.setState ( {updatedStateProperty}, callback())
}
ParentComponent
callback = () => {
this.setState ( {propSentToChild})
}
What happens is the child component changes state, then render occurs, then the callback occurs, prompting another render.
I want to either
A. change child state, then have the callback called before render
or
B. update child state, then ignore the parents passed props
I can do B, but I'm unsure whether it is proper form to basically make the child's version of the shared state the source of truth
I think you're kind of close. What you really want to do is pass the state to the parent, handle setting the state there, and let the new state trickle down to your child component via props. This is a fairly common pattern for react.
class Parent extends React.Component {
constructor() {
this.state = { foo: "bar", bing: "baz" }
}
stateUpdater(newState) {
this.setState({ ...this.state, ...newState });
}
render() {
return <Child
prop1={this.state.foo}
prop2={this.state.baz}
stateUpdater={this.stateUpdater}
/>
}
}
class Child extends React.Component {
handleClick = () => {
this.props.stateUpdater({ foo: 'bazaar' });
}
render() {
return <div>
The foo is {this.props.foo} and the baz is {this.props.baz}.
<button onClick={this.handleClick}>Click Me!</button>
</div>
}
}
If I have this basic layout.
(child 2 has a modal in the page I want to click a button in the modal(located in child2) and have it carry the image(and value) to child1)
app==>
Child1
Child2 (modal) btn Onclick>(img)/btn> (/modal)
You need to have a function in your app which takes the values when changed, passes the value to child 2 through props, and pass that function to child1 and use it in onClick of the button.
I have come up with something like this. See whether its concepts fit your problem.
class Child1 extends React.Component{
handleOnClick = () => {
//your logic
}
render = () => (
<Child2 handleOnClick={handleOnClick}/>
)
}
class Child2 extends React.Component{
constructor(props){
super(props);
this.state = {
value1: '',
value2: '',
}
}
extraLogicForOnClick = () => {
const { handleOnClick } = this.props;
handleOnClick(this.state);
}
render = () => {
<Modal>
<Button onClick={extraLogicForOnClick}/>
</Modal>
}
}
Kowshhal's Answer is correct in that: The solution is to pass a callback function that updates state of the modal's open property (or however you're handling model content visibility) from the Component (i.e. parent) that contains the ModalButton/ModalContent.
However, due to React's Reconciliation mechanism for rendering the DOM, this would be extremely inefficient and require re-rendering the entire application (App) if the callback and state of modal are located at "in your app." More importantly, such a brutal hacking of the DOM contradicts a core competency of React: Fast/Efficient DOM rendering.
If we choose to maintain state and callback in application (as Kowshaal suggests) it would mean application would re-render every time the modal opens --- which means we needlessly re-render Child1 just because Child2 (modal) is changing its child (ModalButon/ModalContent).
Where and How to Manage the open/closed modal state and callback:
(1) Move Child2 (modal) and of course its child (button) to be Children of Child1 rather than App. (2) Add callback and state to handle modal visibility in Child1.
This --crucially -- makes code more extensible say if you want to add new Children to App (siblings to Child1) down the road without having to re-render all of them whenever Child1's modal changes.
Want a Navbar at App Level down the road? A Logo component? Maybe a second modal that triggers different content? Easy --- no matter what's added --- only Child1 will be hacked off thereby saving innocent DOM nodes.
I made some app to get asynchronously data from remote server.
The app just parent component - to get data, and two child components.
One child component for display asynchronous data.
Other child for filter functionality. Just input string where user typing and data in first component display appropriate items.
There are a lot code with console.log everywhere, but in simple scheme it:
class App extends Component {
state = {isLoading:true, query:''}
getData = (location) => {
axios.get(endPoint).then(response=>{ response.map((item) => { places.push(item)})
// ***** first setState
this.setState({isLoading:false})
})
}
updateQuery = (e) => {
// ***** second setState
this.setState({query:e.target.value.trim()})
}
componentDidMount(){
this.getData(location)
}
render() {
if (!this.state.isLoading){
if (this.state.query){
const match = new RegExp(escapeRegExp(this.state.query),'i')
searchTitles = places.filter(function(item){return match.test(item.name)})
}else{
searchTitles = places.slice();
}
}
return (
<div className="App">
<input type='text' onChange={this.updateQuery} value={this.state.query}/>
<List places = {searchTitles}/>
</div>
);
}
}
export default App;
When state change in case of using everything is OK - content refreshed in next child component.
But child component that display data - some items not full of content... no photos and some text information. So probably its rendered before getting remote data.
But why its not re-render it after state.isLoad toggled to 'false' (in code - after got response) ?
I put among code console.log to track processes ... and weird things: state.isLoad switched to false before some part of data came from server. (((
I dont use ShouldComponentUpdate() inside child component.
Per React's documentation for setState
setState() will always lead to a re-render unless
shouldComponentUpdate() returns false.
As mentioned, one way to avoid a re-render is shouldComponentUpdate returning false (shouldComponentUpdate takes in nextProps and nextState) but it's not clear why someone would trigger a state change with setState and then nullify that state change with shouldComponentUpdate.
I'm learning React and I've started extracting components. I understand how to bind an event like onClick on a child component, but what about a grandchild?
Example:
I have a List component. It has ListRow children. Within each ListRow child, I have a button component for deleting that particular row from the parent (List). My thoughts are that thedeleteRowclick handler would be on theListcomponent so that I could then set the state. However, I can't seem to find a way to call the grandparent's (List`) eventHandler.
<List /> // has event handler as well as state for the list items
<ListRow />
<DeleteButton /> //when clicking this i want to delete parent <ListRow />
Am I just supposed to pass the onclick down the chain?
When creating components you have to decide whether or not a component is a functional component or a component that needs to manage state. Here is an example where you have a "Grandparent" that passes down functionality to it's child and the child to it's child. If a component does not need to manage state you make it a "functional component" like the "Parent" and "Child" examples below:
class GrandParent extends Component {
handleState = (obj) => {
this.setState(obj);
}
render() {
return (
<Parent handleState={this.handleState} />
);
}
}
function Parent(props) {
render() {
return (
<Child handleState={props.handleState} />
);
}
}
function Child(props) {
render() {
return (
...
);
}
}
You want to pass it down along and wherever you need to call the function you can use it as props.handleState() from whatever component that you send it to.
You could try something this:
// grandparent function that goes into parent
heirloom()
{
console.log("grandparent says hi");
//something happens
}
// everything else (put into all subsequent children)
heirloom()
{
this.props.heirloom();
}
<List heirloom="this.heirloom">
<ListRow heirloom="this.heirloom" />
<DeleteButton onClick="this.heirloom"/>
My syntax may be off and this may or may not work, I haven't had the chance to play around with React for a while. If it does, great! If it doesn't, let's just hope someone with a better answer comes along ^^
I have a main component App containing some children according to the routes (I use react-router) etc :
class App extends Component {
otherClick = () => { /* run every children's `handleButton2` function */ }
<div className="App">
<Button handleMenuClick={this.toggleSideBar}>Button 1</Button>
<Button handleOtherClick={this.otherClick}>Button 2</Button>
<SideBar ref="sideBar" title="Toto"/>
{this.props.children}
</div>
}
So, according to the route, App will contain some other containers such as:
class ContainerABC extends React.Component {
constructor(props) {
super(props);
}
handleButton2 = () => {
let sc = this.refs.subCont;
sc.setState({visible : !sc.visible});
// Change the color of Button 2 ???
};
render() {
return (
<div>
<SubContainer ref="subCont"/>
</div>
);
}
};
The role of Button 2 depends on the current Container. In the example above, when I have a ContainerABC as child, I want that Button 2 toggles the SubContainer of ContainerABC.
How can I tell to Button 2 to do the appropriate action according to the child of the component ?
And/or how can I modify Button 2 (or any trigger) from SubCont when Button 2 triggers an action on SubCont ?
Maybe using Redux ? I don't see how it could be helpful
Redux might help only because it can trigger an action that, in return, modifies the global state tree (e.g. redux store through a reducer). If that's the only purpose you need fulfilling, then I'd recommend against adding complexity (as much as I fancy Redux).
I assume you want a random child from {this.props.children} fire a random action once Button 2 is clicked?
Let's observe this commonly enforced React pattern:
Properties flow downwards. Actions (read: callbacks) go upwards.
That said, you may want to iterate through your {this.props.children} and check for the existence of a special callback prop that adheres to your API requirements.
React.Children.forEach(this.props.children, (child) => {
if (typeof child.props.toggleButton2State !== "function") {
throw('Woah, cowboy, you need that toggleButton2State function);
}
}
Then your button could cycle through children in the same manner and execute that function, if exists.
handleButton2Click() {
React.Children.forEach(this.props.children, (child) => {
if (typeof child.props.toggleButton2State === "function") {
child.props.toggleButton2State.call(child, !oldState, this);
}
}
}
So you just called child's callback function in scope of the child with boolean state being toggled and you also passed the reference to the parent component (this).
I would strongly suggest you never manipulate the parent container from a child. You never know how your hierarchy may change.
Obviously, this is a very rough example but it should get you going. Let me know how it goes.
If the behavior of the button depends on what container is being rendered, then it sounds to me like the container should render the buttons. You could wire up some props (could even use cloneElement to put them on the children) so you can pass callbacks down that would change the behavior of the button, but that sounds like a nightmare to maintain.
You could put those buttons in a separate component (with a prop to determine what they do) and render it in the containers. That sounds much simpler to me.