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.
Related
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 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();
}
}
I am working since more than a year with React and i have read Thinking in react, Lifting state up, and State and lifecycle.
I have learned that React's concept with data flow is is One-way data flow.
Citates from these pages:
React’s one-way data flow (also called one-way binding) keeps everything modular and fast.
Remember: React is all about one-way data flow down the component hierarchy. It may not be immediately clear which component should own what state. This is often the most challenging part for newcomers to understand, so follow these steps to figure it out:...
If you imagine a component tree as a waterfall of props, each component’s state is like an additional water source that joins it at an arbitrary point but also flows down.
As i understand this, following example is not allowed because i am passing child state data to the parent. But i see some developers working like that:
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = { fromParent: null };
}
addSomething(stateValueFromChild) {
this.setState({fromParent: stateValueFromChild});
}
render() {
return <Child
addSomething={(stateValueFromChild) => this.addSomething(stateValueFromChild)}>
// ...
</Child>;
}
}
class Child extends React.Component {
constructor(props) {
super(props);
this.state = { fromChild: 'foo' };
}
render() {
return <Form onSubmit={() => this.props.addSomething(this.state.fromChild)}>
// ...
</Form>;
}
}
My questions now are:
Is this really not allowed?
Why should this not be modular and fast?
Is this really braking the one-way-dataflow, becoming a two way dataflow?
What other problems could happen with this way?
When i would lift the state up, how would you solve following case; 50 concrete parents that uses that child component, should every parent have a same initialized sub-state for the same child that they are using?
Is this really not allowed?
Why should this not be modular and fast?
Excellent questions. This is allowed. It's just a bit tricky to make it work right because you've got state synchronization here. In the modern frontend world, the state synchronization is believed to be a very challenging task.
The problem will appear when you'll need to sync the state in two directions. For instance, if this child form is used to edit some element of the list, and you're changing the current element of the list. You'll need the child component to detect that situation and sync its local state with the new element from props during the particular UI update. As long as you don't have that, you're fine.
Is this really braking the one-way-dataflow, becoming a two way dataflow?
Nah, it's not. It's still unidirectional data flow because React can't work in any other way by design; UI updates always coming from top to bottom. In your example, your child triggers an event which causes the parent to update its state (it's totally fine), which will cause the UI update of the parent and the child. If you really violate "unidirectional data flow", you'll feel it. You will get an infinite loop or something similar.
When i would lift the state up, how would you solve following case; 50 concrete parents that uses that child component, should every parent have a same initialized sub-state for the same child that they are using?
Yep, that's what they mean by "lifting the state". You organize your root state as a tree reflecting the state of the children, then pass down elements of the state to children along with callbacks to modify the root state.
It's allowed and there is nothing wrong with your code, but I would not call it passing state from child to parent. All you do is invoking method passed in props and triggered by event with some argument, which in your example is child's state value, but it could be any other variable. Parent component knows nothing about nature of this argument, it just receives the value and able to do anything with it, for example change it's own state to another. If Child's state will change, Parent is not going to receive this update without onSubmit event fired again. But children always receive updates from the parent and automatically get rerendered, when props get changed. And of course some of the props could be states of some parents. Here is the major difference in behavior.
There is a good article explaining this in details: Props down, Events Up
Your question is absolutely correct, many times developer (including myself) struggled for passing child's components state or props to parent component.
I always do logic for getting next state or next props in child component and pass next state or next props to parent component by using handler functions of parent component.
import React, { Component } from "react";
import { render } from "react-dom";
class Parent extends Component {
constructor(props) {
super(props);
this.handleSomething = this.handleSomething.bind(this); // binding method
this.state = {
fromParent: "foo"
};
}
handleSomething(value) {
this.setState(prevState => {
return {
fromParent: value
};
});
}
render() {
return (
<div>
<h1>State: {this.state.fromParent}</h1>
<Child handleSomething={this.handleSomething} />
</div>
);
}
}
class Child extends Component {
constructor(props) {
super(props);
this.state = {
fromChild: "bar"
};
}
render() {
return (
<div>
<button
onClick={e => {
const { fromChild } = this.state;
// do whatever as per your logic for get value from child pass to handleSomething function
// you can also do same for handling forms
this.props.handleSomething(fromChild);
}}
>
Click Me
</button>
</div>
);
}
}
render(<Parent />, document.getElementById("app"));
I have a need to pull some content out of a child component such that the parent (or just generally higher-level) component can dispatch an action creator with the data of the inner child component.
Previously I was invoking a function on the child ref like this.refs.child.doStuff() until I found it was becoming difficult to keep track of the ref in whatever parent component I needed the method data in. Everytime there is a HOC or some other kind of compositing wrapper I need to add more code to pass the ref up the chain that I need. Not to mention there was a lot of duplicate code in each child function that was standard to all.
// child.js
class InnerComponent extends React.PureComponent {
doStuff = () => {
calculateFrom(this.state.content)
// and then do stuff with it..
// I've since made this more redux-y by just returning the data
// out to an action dispatcher
}
}
const SomeHOC = (args) => {
return (Component) => class extends React.Component {
proc(wrappedComponentInstance) {
// I went with using the getWrappedInstance func here
// to mimic conect(.., .., .. { withRef: true }) for
// basic compatibility with a higher level function that
// dives through the wrappedInstances to get to the bottom one
this.getWrappedInstance = () => wrappedComponentInstance;
}
render() {
const props = Object.assign({}, this.props, { ref: this.proc.bind(this) });
return <Component {...props} {...this.state} />
}
}
}
// I actually don't need the connect() here but will on other components of the same style when they're not in their wrapped form
export default connect(..., ..., null, { withRef: true })(SomeHOC()(InnerComponent);
// parent container (there are multiples of these in my app)
class Container extends React.PureComponent {
determineContent = (...) => {
return React.createElement(this.state.content, {
// so I can get to the composited inner element...
ref: (element) => { this._compositeElement = element; },
...viewletProps
});
}
componentWillMount() {
// ...
System.import(`${dynamic}.jsx`).then((content) => {
this.setState({ content });
});
}
renderButtonContainer = () => {
// Here's where things get weird...
if (!this.doStuff) {
this.doStuff = ((() => {
// hook into my ref
const compositeElement = this._compositeElement;
// deep-dive through to get to the base
let base;
if (compositeElement && typeof compositeElement.getWrappedInstance === 'function') {
base = this._compositeElement.getWrappedInstance();
while (typeof base.getWrappedInstance === 'function') {
base = base.getWrappedInstance();
}
if (typeof compositeElement.doStuff === 'function') {
this.doStuff = base.doStuff;
}
}
})());
}
return (
<div hidden={!this.doStuff}>
<span><i class="myIcon" onClick={this.doStuff}></i></span>
</div>
);
}
render() {
<div>
{ this.renderButtonContainer() }
<div>
{ this.determineContent(...) }
</div>
</div>
}
}
I've since pulled all of that out and am now dispatching an action per Redux-style and letting my reducer handle what needs to happen (just a synchronous internal call that executes immediately using the data in the action; I'm still unsure if this being in the reducer is bad form)
However, since I need my child components to still return the invocation of their doStuff() (getStuff() instead) at the time of the parent component's choosing, I find myself stuck with my same ref issues.
Am I going about this all wrong? It almost seems to me like for each child component I have I need to be storing this always-changing data from getStuff() inside my state model and just pass it down into the component? But I'd anticipate that would be too disassociating from the actual component and the rest of my app doesn't really care about that.
Frankly, none of that seems like a good idea at all. Even if it's technically possible, it completely goes against the intended usage of both React and Redux.
Refs in general are an escape hatch, and should be used only if necessary. Refs to DOM nodes are more useful, because you may need to do things like determining if a click is inside a DOM node, or read a value from an uncontrolled input. Refs to components have a lot fewer use cases. In particular, directly calling methods on components is definitely not idiomatic React usage, and should be avoided if at all possible. From there, groveling into the guts of React's implementation is a really bad idea.
If you need to make use of a nested child's data in a parent component, and those are widely separated, then you should either pass some kind of callback prop down through all those children, or you should dispatch a Redux action to put the data into the store and have the parent subscribe to that data.
So yeah, it's hard to tell exactly what you're actually needing to get done from that description and example code, but I can safely say that what you've got there is not the right way to do it.
#markerikson there is valid reasons for doing this. React unidirectional data flow is a very important fundamental rule, but just like how the best music breaks some rules (jazz improv), so too do the best React apps break some conventions in very crafty places. For example, Redux and React router must break convention by using context. So too is there valid use cases for using refs and accessing components with refs.
For example: Imagine you have a very complicated text editor component as a parent. You have many child components responsible for the fields such as a contact field a website field and a description field etc. Now, inside each of these child components you frequently update state. Specifically, on every single click of a button you update the state. The product ask is to have all of the fields alive at the same time so you cannot have individual submit buttons on the fields. You must take all of the current state of all fields at the same time. Now, you could manage the state by lifting it to the complicated parent component, but then that means every single time you press a button to re-render any of these fields you are going to have a callback that causes the complex parent to rerender and therefore all other children components.
On the other hand, with the crafty use of the nifty trick component refs we can update the state of all fields completely independently without triggering rerenders in everything, then when the user submits the form we can trigger one function inside the parent that checks the current/final state of all children through refs and submit that value to the database.
The idea of antipatterns comes up in React a lot. You are right to stick to the rules most of the time, but like a jazz pianist takes the standard Saints Go Marching In and breaks some rules to make something more enjoyable to some people, we can take React, a rigid set of conventions and improv on them if it creates a better experience for certain contexts.
I have a listview component which consists of a number of child listitem components.
Each child listitem have a showSubMenu boolean state, which display a few extra buttons next to the list item.
This state should update in response to a user event, say, a click on the component DOM node.
childcomponent:
_handleClick() {
... mutate state
this.props.onClick() // call the onClick handler provided by the parent to update the state in parent
}
However, it feels somewhat wrong to update state like, as it mutates state in different places.
The other way i figured i could accomplish it was to call the this.props.onClick directly, and move the child state into the parent as a prop instead, and then do change the state there, and trickle it down as props.
Which, if any, of these approaches is idiomatic or preferable?
First of all, I think that the question's title doesn't describe very well what's your doubt. Is more an issue about where the state should go.
The theory of React says that you should put your state in the higher component that you can find for being the single source of truth for a set of components.
For each piece of state in your application:
Identify every component that renders something based on that state.
Find a common owner component (a single component above all the
components that need the state in the hierarchy).
Either the common
owner or another component higher up in the hierarchy should own the
state.
If you can't find a component where it makes sense to own the
state, create a new component simply for holding the state and add it
somewhere in the hierarchy above the common owner component.
However, a Software Engineer at Facebook said:
We started with large top level components which pull all the data
needed for their children, and pass it down through props. This leads
to a lot of cruft and irrelevant code in the intermediate components.
What we settled on, for the most part, is components declaring and
fetching the data they need themselves...
Sure, is talking about data fetched from stores but what im traying to say is that in some cases the theory is not the best option.
In this case i would say that the showSubMenu state only have sense for the list item to show a couple of buttons so its a good option put that state in the child component. I say is a good option because is a simple solution for a simple problem, the other option that you propose means having something like this:
var GroceryList = React.createClass({
handleClick: function(i) {
console.log('You clicked: ' + this.props.items[i]);
},
render: function() {
return (
<div>
{this.props.items.map(function(item, i) {
return (
<div onClick={this.handleClick.bind(this, i)} key={i}>{item} </div>
);
}, this)}
</div>
);
}
});
If, in a future, the list view has to get acknowledge of that state to show something for example, the state should be in the parent component.
However, i think it's a thin line and you can do wathever makes sense in your specific case, I have a very similar case in my app and it's a simple case so i put the state in the child. Tomorrow maybe i must change it and put the state in his parent.
With many components depending on same state and its mutation you will encounter two issues.
They are placed in component tree so far away that your state will have to be stored in a parent component very high up in the render tree.
Placing the state very high far away from children components you will have to pass them down through many components that should not be aware of this state.
THERE ARE TWO SOLUTIONS FOR THIS ISSUE!
Use React.createContext and user context provider to pass the data to child elements.
Use redux, and react-redux libraries to save your state in store and connect it to different components in your app. For your information react-redux library uses React.createContext methods under the hood.
EXAMPLES:
Create Context
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
// Use a Provider to pass the current theme to the tree below.
// Any component can read it, no matter how deep it is.
// In this example, we're passing "dark" as the current value.
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
class ThemedButton extends React.Component {
// Assign a contextType to read the current theme context.
// React will find the closest theme Provider above and use its value.
// In this example, the current theme is "dark".
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
}
// A component in the middle doesn't have to
// pass the theme down explicitly anymore.
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
// Assign a contextType to read the current theme context.
// React will find the closest theme Provider above and use its value.
// In this example, the current theme is "dark".
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
REDUX AND REACT-REDUX
import { connect } from 'react-redux'
const App = props => {
return <div>{props.user}</div>
}
const mapStateToProps = state => {
return state
}
export default connect(mapStateToProps)(App)
For more information about redux and react-redux check out this link:
https://redux.js.org/recipes/writing-tests#connected-components