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 :)
Related
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
My page contains two completely separate React components (different files, different classes, no parent-child relationship).
How can one component call an instance method in another component? The problem seems to be obtaining the instance of the target component.
EDIT: Both components share the same parent (i.e. they are rendered in the same render() method) but I still don't know how to pass the reference of the target component to the calling component.
The short answer is: they don't.
It's not clear what you're trying to accomplish, so I can't speak to the specifics of your case, but the way React components "communicate" with one another is via state and props. For example, consider a Page component that has two child components, CompA and CompB, rendered something like this:
<Page>
<CompA />
<CompB />
</Page>
If CompA needs to pass something to CompB, this is done through state on the Page component, with that state exposed as props on CompA and CompB, something like this:
class Page extends React.Component {
constructor(props) {
super(props);
this.state = {
sharedValue: 42,
};
}
onChangeSharedValue(newValue) {
this.setState({ sharedValue: newValue });
}
render() {
return (
<div>
<CompA
sharedValue={this.state.sharedValue}
onChange={this.onChangeSharedValue}
/>
<CompB
sharedValue={this.state.sharedValue}
onChange={this.onChangeSharedValue}
/>
</div>
);
}
}
If CompA needs to change the shared value, it calls the onChange handler, which will change the state on the Page component. That value will then be propagated down to the CompB component.
There is no direct communication between components like you're describing; it is all done via state and props.
"Props down, Events up."
If you provide us a specific example of what you're looking for, I can update this post with a more specific response.
But in general, there are a couple of strategies that you can take. Some of them are presented here.
The preferred approach is to simply move your calling method to the parent component. It's a common strategy in React.
If you're not able to, then the next step would be to write an event handler for the parent, and then pass this event down to the first child component.
Use this event to pass information up to the parent, so that when it gets triggered, data can be passed as props down to the second component.
I only recently started doing React development and I found a solution for this problem that suits me. Admittedly, I haven't seen it referenced anywhere and when I showed it to a colleague who's been doing React for years, he kinda furrowed his brow and felt that it wasn't "right", but he couldn't really articulate to me why it's "wrong". I'm sure I'll be shouted down for it here, but I thought I'd share anyway:
File #1: objects.js
let objects= {};
export default objects;
File #2: firstComponent.js
import React from 'react';
import objects from 'objects';
class FirstComponent extends React.Component {
constructor(props) {
super(props);
objects['FirstComponent'] = this; // store a reference to this component in 'objects'
}
doSomethingInFirstComponent() {
console.log('did something in first component');
}
render() {
return (<div></div>);
}
}
export default FirstComponent;
File #3: secondComponent.js
import React from 'react';
import objects from 'objects';
class SecondComponent extends React.Component {
render() {
objects.FirstComponent.doSomethingInFirstComponent(); // call the method on the component referred to in 'objects'
return (<div></div>);
}
}
export default SecondComponent ;
When SecondComponent renders, it will trigger the console.log() in FirstComponent.doSomethingInFirstComponent(). This assumes, of course, that FirstComponent is actually mounted.
The "React Guys" that I know seem to think this approach is somehow evil. It uses a simple JavaScript object outside the normal React scope to maintain a reference to any existing objects that I choose to store there. Other than them telling me that "this isn't the way you do things in React", I haven't yet found a good explanation for how this will break or otherwise screw-up my app. I use it as a low-grade replacement for massive-overkill state-management tools like Redux. I also use it to avoid having to pass properties down through dozens of layers of React components just so something at the last level can trigger something waaaaay up in the first level.
That's not to say this approach doesn't have it's problems:
It creates an obvious dependency between the generic objects object, any component that is designed to store a reference to itself inside objects, and any component that wishes to utilizes those references. Then again, using any kind of global state-management solution creates a similar dependency.
It's probably a bad solution if you have any doubt that FirstComponent will be mounted before you try to call it from within SecondComponent.
I've found that just having the reference to a React component won't allow you to do all the things that React components can do natively. For example, it won't work to call objects.FirstComponent.setState(). You can call a method in FirstComponent, which in turn can invoke its own setState(), but you can't invoke FirstComponent's setState() directly from within SecondComponent. Quite frankly, I think this is a good thing.
You can, however, directly access the state values from the components referenced in objects.
This should only be done with "global" components (components that functionally serve as singletons). If, for example, you had a simple UI component called BasicSpan that did little more than render a basic span tag, and you proceeded to use that component over and over again throughout your React app, I'm sure it would quickly become an unmanageable nightmare to try to place references to these simple components in the objects object and then try to intelligently manage calls to those components' internal methods.
you can send an event as props and call it from other component.
Say you have a class
Class A{
handleChange(evt)
{
this.setState({
name:evt.target.value
})
}
render{
return(
<div>
<ComponentB name={this.state.name}{ onChange={this.handleChange}/>
</div>
);
}
}
Child Component
Class B{
handleChange()
{
//logic
}
render{
return(
<div>
<input type="text" onChange={this.props.onChange}/>
{this.props.name}
</div>
);
}
Here in Component B when you change the input it will call the method
of class A and update state of A.
Now getting the updated state as props in component B will give you
the changed text that you just entered
The problem I ran into is this: I want to have multiple buttons that open the same modal, and depending on which button is pressed, the modal will have different content.
The buttons are placed in a Component and the modal is placed in another one (also, they are not in a parent-child relationship).
I have a "state object" that saves info about which button was clicked and whether the modal is opened or closed. This object needs to be read AND modified by BOTH components.
In Angular, I would create a Service that manages the state of the buttons and the modal by injecting the "state object" into both components.
How would I solve this in React?
(I read somewhere that this can be accomplished by using Redux, but at the moment my app doesn't implement the flux architecture and I'd need a solution that doesn't require flux/redux)
One of the core ideas of Redux is to have a global state and only components very high up in the DOM tree will connect to it. You can simulate this idea in React without Redux by shifting the state higher up into the component tree, and this state will essentially become your "Redux store".
Find a common ancestor of these components (preferably lowest one) that you want to read/modify the object, and save the object as state in that component. There definitely will be a common ancestor (the root) for every pair of components which are not root elements.
This common ancestor should define a method that is passed down into these components as props so that these child components can call the actions and modify the state within the ancestor.
Some example code is shown below. Let me know if you need further clarification.
class Ancestor extends React.Component {
constructor(props) {
super(props);
this.state = { object: null };
}
someMethod(data) {
this.setState({
object: data
});
}
render() {
return (
<div>
<div>
<Button clickHandler={this.someMethod.bind(this)}/>
<Button clickHandler={this.someMethod.bind(this)}/>
</div>
<Modal data={this.state.object} someProp={this.someMethod.bind(this)}/>
</div>
)
}
}
class Button extends React.Component {
doSomething() {
this.props.clickHandler(someData);
}
render() {
return (
<button onClick={this.doSomething.bind(this)}/>Button</button>
);
}
}
Normally your case is a perfect case for using redux. But since you need a non-flux/redux solution, you should take a look at how to use React context Link to Context docs
Occasionally, you want to pass data through the component tree without
having to pass the props down manually at every level. React's
"context" feature lets you do this.
Only requirement to use Context is that it needs a wrapper parent component. If you don't have one, you need to add one. Wrapper component in your route could solve this.
If you can't use Redux the only solution I see is to pass model to all components. Your data should be stored as separate object (model) which implements all possible changes user can take.
e.g.
app.model = {
addItem: function() { .. }
update: function() { .. }
}
Then you can pass this object as a props to your components.
<MyButton model={app.model}>Add Item</MyButton>
var MyButton = React.createClass({
render: function() {
return (
<div className="coolButton" onClick={this.props.model.addItem}>
{this.props.children}
</div>
})
Information if modal is opened or not you should keep in state of modal or its parent.
var MyModal = React.createClass({
getInitialState: function() {
return {isShown: false};
},
render: function() {
if (this.state.isShow===false) {
return
}
return (
<div className="modal">
{this.props.children}
</div>
})
The short answer - you can't do this w/o holding state on in the top-level component, this is a limitation due to the fact that React offers parent-child connection only.
There is a lot of different solutions addressed to fix this issue. People use containers like react-redux to share the common state. The problem with them is that you still has to do some work and answer some questions on how to compose the state and connect it.
This is why I made a more general solution which allows you to "mount" component even from different branch of three. The solution feels like a symlink in filesystem. More detailed problem definition, src and examples can be found here: https://github.com/fckt/react-layer-stack#rationale
Rationale
react/react-dom comes comes with 2 basic assumptions/ideas:
every UI is hierarchical naturally. This why we have the idea of components which wrap each other
react-dom mounts (physically) child component to its parent DOM node by default
The problem is that sometimes the second property isn't what you want
in your case. Sometimes you want to mount your component into
different physical DOM node and hold logical connection between
parent and child at the same time.
Canonical example is Tooltip-like component: at some point of
development process you could find that you need to add some
description for your UI element: it'll render in fixed layer and
should know its coordinates (which are that UI element coord or
mouse coords) and at the same time it needs information whether it
needs to be shown right now or not, its content and some context from
parent components. This example shows that sometimes logical hierarchy
isn't match with the physical DOM hierarchy.
Take a look at https://github.com/fckt/react-layer-stack/blob/master/README.md#real-world-usage-example to take a look at the concrete example (you're able to use logically top-level state inside the Layer):
import React, { Component } from 'react';
import { Layer, LayerContext } from 'react-layer-stack';
import FixedLayer from './demo/components/FixedLayer';
class Demo extends Component {
render() {
return (
<div>
<Layer use={ [this.state.counter] } id="lightbox2">{ (_, content) =>
<FixedLayer style={ { marginRight: '15px', marginBottom: '15px' } }>
{ content } { this.state.counter }
</FixedLayer>
}</Layer>
<LayerContext id="lightbox2">{({ showMe, hideMe }) => (
<button onMouseLeave={ hideMe } onMouseMove={ ({ pageX, pageY }) => {
showMe(
<div style={{
left: pageX, top: pageY + 20, position: "absolute",
padding: '10px',
background: 'rgba(0,0,0,0.7)', color: '#fff', borderRadius: '5px',
boxShadow: '0px 0px 50px 0px rgba(0,0,0,0.60)'}}>
“There has to be message triage. If you say three things, you don’t say anything.”
</div>)
}}>Yet another button. Move your pointer to it.</button> )}
</LayerContext>
</div>
)
}
}
I am looking to validate a form with Redux. I am trying to use make a form component which will iterate through children and find various input components (not to be confused with a native <input>.
I know there are a lot of open source solutions, but I'd like to understand some mechanics before jumping into picking any. I have a Form component setup to test like this:
import React from 'react';
export default class Component extends React.Component {
componentDidMount() {
this._iterate(this.props.children);
}
render(){
return (
<form {...this.props}>{this.props.children}</form>
);
}
_iterate(children) {
React.Children.forEach(children, child => {
console.log(child);
if (child.props.children) {
console.log('get children');
this._iterate(child.props.children);
}
});
}
};
I then have another Component with a render like this:
render() {
return (
<div>
<Form>
<ComponentA />
<ComponentB />
</Form>
</div>
);
}
Now ComponentA or ComponentB might have a component that nests more components down the line. Within those components would be a React component I have made for Text, Select, etc.
The code above would just console.log the components, and any children of them, that are in this specific render. It does not jump down into ComponentA children.
Is there a solution to that?
This isn't a problem you really want to solve.
The power in react is largely around the design pattern it encourages, and what you're doing is breaking that pattern; Component's should only talk to their immediate children and respond to their immediate parents. If you need to go deeper than that, then the component in the middle needs to be responsible for passing that data.
Rather than trying to dig into the innards of ComponentA and ComponentB, those component's themselves should have the accessibility props that you need. I.e., <ComponentA onChange={whatever} errorMessage={whatever}/> etc. and then hooking those props to their children should occur within ComponentA.
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