I'm trying to set up state for a portion of my web game that allows players to upload photos of their monsters.
I have a parent component called <MonsterContainer>. It contains two child components: <MonsterPortraitUpload> and <MonsterViewer>
I am setting state in <MonsterContainer>
<MonsterPortraitUpload> Will allow users to upload photos and it will add to the MonsterPortraits array in the state.
<MonsterViewer> Will show all the photos of the monster in the array.
How can I ensure that my two child components have access to this state?
Here is what I have now:
class MonsterContainer extends Component {
constructor() {
super();
this.state = {
MonsterPortraits: []
};
}
render() {
const { monsterId } = this.props;
return (
<div>
<div>
<MonsterPortraitUpload monsterId={monsterId}/>
</div>
<div>
<MonsterViewer monsterId={monsterId}/>
</div>
</div>
);
}
}
Right now I am trying to write the state to the console in <MonsterViewer> but I get this error:
Uncaught TypeError: Cannot read property 'MonsterPortraits' of null
Child components don't have access to parent state; data flows one way: down.
If a child component needs access to parent state that state should be passed as a property.
If that is insufficient for your needs (e.g., a deeply-nested hierarchy where children need to update state at a much higher level) then you'll probably want a different state management system, e.g., Redux/etc.
In your case, as presented, passing down the array of portraits to <MonsterView /> would seem sufficient, and <MonsterPortraitUpload /> would take a function prop that adds to the container's state (e.g., add a portrait).
This is a fundamental concept in React and similar: dumb components, smart containers. Dump components take data and functions, containers manage state. This breaks down in deep hierarchies because you end up having to pass properties down multiple levels.
That may be address via React Contexts, or injecting properties into cloned children, etc., but whether or not those are good solutions depends on your architecture. In general, a different form of state management can make problems like this go away by introducing a thin layer of complexity.
To modify the container's state you pass a hander; very off-the-cuff:
class MonsterContainer extends Component {
// Etc.
addPortrait(someData) {
this.setState([ ...this.state.MonsterPortraits, someData ]);
}
render() {
// Etc.
<MonsterPortraitUpload addHandler={this.addPortrait} />
In <MonsterPortraitUpload /> you'd create the data however (e.g., a form) and then on a user action (e.g., a button press) you call the container's handler function passing whatever data:
class MonsterPortraitUpload extends Component {
addPortait() {
// Marshall the data however, then
this.props.addPortrait(newData);
}
// Etc.
}
An error that you show, definitely not from attached code.
If you need to pass state down use this
class MonsterContainer extends Component {
constructor() {
super();
this.state = {
MonsterPortraits: []
};
}
render() {
return (
<div>
<div>
<MonsterPortraitUpload monsterPortraits={this.state.MonsterPortraits}/>
</div>
<div>
<MonsterViewer monsterPortraits={this.state.MonsterPortraits}/>
</div>
</div>
);
}
}
So in MonsterPortraitUpload component you may use this.props.monsterPortraits
Related
I'm currently developing an app that uses React in some parts of its UI to implement some complex feature that are very interactive. Some of these parts of the app, that are in fact React components, have become very complex in size.
This has led me to organize the code by dividing them in multiple subcomponents (and those in their own subcomponents, and so forth). The main problem is that some of the grandchildren of the main component might have the need to modify the state of another grandchildren that they're not related to. To do so, I end up having to have the state in the common parent and passing a lot of event handlers (methods of the parent component) to their children, and those children would need to pass it to their grandchildren.
As you can imagine, this is becoming some kind of a mess.
// MyComponent.js
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
list: [1, 2, 3, 4],
selected: '',
}
this.add = this.add.bind(this)
this.handleChange = this.handleChange.bind(this)
}
add() {
const newNumber = this.state.list[this.state.list.length - 1] + 1,
list = [...this.state.list, newNumber]
this.setState({list})
}
handleChange({target}) {
this.setState({
selected: target.value,
})
}
render() {
return (
<>
<List items={this.state.list} selected={this.state.selected} />
<Button onClick={this.add} />
<input type="text" value={this.state.selected} onChange={this.handleChange} />
</>
)
}
}
// Button.js
class Button extends React.Component {
render() {
return (
<button onClick={this.props.onClick}>Click me!</button>
);
}
}
// List.js
class List extends React.Component {
constructor(props) {
super(props)
this.refs = props.items.map(_ => React.createRef())
}
render() {
return (
<ul>
{this.props.items.map((item, key) =>
(<li ref={this.ref[key]} key={key}>{item}</li>)
)}
</ul>
);
}
}
In the previous dummy code you can see how I need to define the add() method in the MyCompoent component so that an action that happens in the Button component can modify what is being shown in List. Even tho this might seem like the obvious way to do it, my component has a big component tree, and a lot of methods, and most of then are lost in the tree, passing from parent to child until it reaches the component that should be expected.
I have done some research on the internet and it turns out this is a very common problem. In most sites, using Redux or other state management library is recommended. However, all the tutorials and guides I've seen that implement Redux with React seem to assume you're only using React to build your app, in Single Page Application sort of way. This is not my case.
Is there any way to share the state of a component to avoid this kind of problem? Is there, maybe, a way to use Redux multiple times for multiple components in the same app, where one store saves only the state for MyComponent and can be accessed by either List or any of its possible children?
Redux doesn't require your entire site to be in React. It implements a higher-level component that you can use with any React components even if they are embedded in another site.
You can look at React hooks to solve similar problems. Specifically, check out useContext() and useState().
You've used a lifting state up pattern in react in your example.
It's quite common you good approach but when you app is growing you need to pass all bunch of props throu the tree of components. It's difficult to maintain.
In this case you need to check out redux with separated store or useContext() hook.
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've seen some code examples on github that uses some different kind of props.
I saw childContextTypes and context.
But the implementation is different, though the usage looks similar.
some code looks like this:
propTypes: {
a: React.PropTypes.string
},
childContextTypes: {
a: React.PropTypes.string
},
getChildContext() {
return {
a: this.props.a
}
}
contextTypes: {
a: React.PropTypes.string,
b: React.PropTypes.string
},
render() {
return (
<div>
Three
({this.context.a}, {this.context.b})
</div>
);
}
I've read about it on the net and in stack-overflow but could not understand what is it exactly and where or why to use it?
Why there are more examples of props then contextTypes?
UPDATE - March 29, 2018
Since react v16.3.0, a new context API was released and is considered "safe" to use. though you should still think twice before using it:
Context is primarily used when some data needs to be accessible by many components at different nesting levels. Apply it sparingly because it makes component reuse more difficult.
If you only want to avoid passing some props through many levels, component composition is often a simpler solution than context.
Before you read further let me quote something from React context DOCS
If you want your application to be stable, don't use context. It is an
experimental API and it is likely to break in future releases of
React.
Now it's !safe to read further.
You can use the context API in order to access data that exists in the parents scope, without passing it down to the child component.
This is useful when you don't want to pass down the data manually on each level.
For example, given this scenario:
<Root/> component that renders a child <List/> component.
<List/> component renders a collection of Item components
<Item/> renders a <Button/> (among other components).
Now lets say that the Button component needs certain data from the Root component, like isEnabled which will render a disabled or enabled Button.
This kind of data is set on the top level component the <Root/>, but in order to pass it down to the Button component we will need to pass it down on each level:
<Root/> -> <List isEnabled /> -> <Item isEnabled /> -> <Button isEnabled/>
Well, this is kinda tedious and irrelevant for all other components down the tree.
With the context API you can "skip" this tree flow of passing this data as prop and expose this data in the context object at the top level Root component, then access it directly within the Button component via the context object. You can think of it as if the context is in a shared scope of the parent and child components.
You can also do Parent-Child Coupling, And as the docs mentions, some libraries like react-router use this API in order to pass the data upwards from child components to the container.
Context are really different in use and definition than Props.
Where to use it? Well, if you can survive without it, it means that you don't really need it.
Context values are passed from the parent-that-declared-it, and accessible to all of their children, all of them in the entire app tree, if they "ask" for.
A good example is how <Provider /> in Redux works, so you declare a lot of children, and the connected components (components that you passed throughout connect() internally ask for this context, so no matter where is declared, if this component is inside Provider, it has access to Provider context. And you doesn't have to manually pass all the store through components.
class MyProvider extends React.Component {
getChildContext() {
return {
color: "#6257af"
}
}
render() {
return (
<div>
{this.props.children}
</div>
)
}
}
MyProvider.childContextTypes = {
color: window.PropTypes.string
}
class Main extends React.Component {
render() {
return (
<Text />
)
}
}
class Text extends React.Component {
render() {
return (
<p style={{ color: this.context.color }}>Hi! Context color {this.context.color}</p>
)
}
}
Text.contextTypes = {
color: window.PropTypes.string
}
ReactDOM.render(
<MyProvider>
<Main />
</MyProvider>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://unpkg.com/prop-types/prop-types.js"></script>
<div id="root"></div>
Hope it helps :)
Here there is app root component passing a handleHeaderTitle function to all its children by cloning.
Inside children components, the function is called in componentWillMount()
and as a result root component updates header text based on route.
Root :
{
this.props.children &&
React.cloneElement(this.props.children, {
handleHeaderTitle: this.handleHeaderTitle
})
}
handleHeaderTitle(title) {
this.setState({ headerTitle: title });
}
Child:
componentWillMount() {
this.props.handleHeaderTitle("Profile");
}
Profile.propTypes = { handleHeaderTitle: React.PropTypes.func };
Is this right usecase for adding redux since the state is not really local here as is being set from a child component on its parent ?
I think I will need to create actions like SET_PROFILE_HEADER and inside reducers, combine these. But still figuring out how to inform container root component that child component has changed the header title
To answer your question directly, you don't need Redux to solve your problem. Redux helps you when you have to maintain a lot of state, or when your state needs to be reflected parts of the application that don't have a close common ancestor. In other words, when it gets too cumbersome to use good old fashioned React state.
A more common approach to your specific problem in React would be to include your layout in the child component instead of the root. This avoids the issue of trying to get the child to control content in the parent. It helps preserve the "unidirectional data flow" which is what React is all about.
class Page extends React.Component {
render() {
return (
<div>
<div className='profile-header'>
{ this.props.headerTitle }
</div>
<div className='profile-content'>
{ this.props.children }
</div>
</div>
)
}
}
class MyFirstPage extends React.Component {
render() {
return (
<Page headerTitle='Header Title 1'>
<div>My content here</div>
</Page>
)
}
}
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