I am currently going through the documentation of React.js and have a question about this.props, which according to the docs should be considered immutable and only pushed downwards down the ownership tree since bubbling custom events upwards is discouraged.
Say that I have a UI where the state of a component in the header of the page should be shared with another component that is nested somewhere in the body of the page, let's take a simple checkbox that represents some specific state that will influence the visibility of some nested spans or divs.
The only I way I currently see this achieved is by creating a state property that is pushed downwards from the top element to the child elements.
The two related questions I have with this is:
Does this mean that I should create one component that owns the entire page? (Is rendering the entire page with a single owner component an acceptable thing to do? I understand the concepts of Virtual DOM and diffing so I assume it's not a problem, still I'd like some confirmation in case I miss out on something relevant);
Is it ok to change the property on this.props when clicking the checkbox, in order to re-render the other components on the page? This doesn't make the props immutable (perhaps they mean just that setting new props on this.props down the chain is not accepted to avoid an untransparent stack trace in case of bugs, but changing the value of a state property is?).
Some confirmation would be appreciated.
Thanks.
1) It is fine to have one parent for the whole page, but is not always necessary. It depends on if it is necessary to share the state between components.
2) You never want to alter props via this.props.someValue = newValue. If you need to modify the parent state from a child component, it should always be done with a callback. The example below shows how to share the checkbox state between multiple components using the callback function handleClick to modify the state of is_checked.
JSFiddle of example: https://jsfiddle.net/mark1z/o7noph4y/
var Parent = React.createClass({
getInitialState: function(){
return ({is_checked: 0})
},
handleClick: function(){
this.setState({is_checked: !this.state.is_checked})
},
render: function(){
return (
<div>
<CheckBox is_checked={this.state.is_checked} handleClick={this.handleClick}/>
<OtherComponent is_checked={this.state.is_checked} />
</div>
);
}
});
var CheckBox = React.createClass({
render: function() {
return (
<input type="checkbox" onChange={this.props.handleClick}> Show other component </input>
);
}
});
var OtherComponent = React.createClass({
render: function() {
return (
<div style={{marginTop: 20}}>
{this.props.is_checked ? 'The checkbox is ticked' : 'The checkbox is not ticked'}
</div>
);
}
});
React.render(<Parent />, document.getElementById('container'));
I guess having one master component isn't an issue. The docs suggest that you find the topmost component that can supply it's children with the needed data - and this could easily be the toplevel master component. As I understand this you would have a master component for your main page - that should be the only one that uses state, the children just render what they are given in props. So no, props should not be altered by a child that doesn't own the data, it is the topmost components prerogative to do so. Let's say you have another widget on the page that only cares for a distinct set of data you would make this the root of another tree that fetches data and sets it's state and the props of it's children.
Here is a crappy graph for this situation:
App -(props)-> ItemList -(props)-> Item -(props)-> Photo
+ + |
+ ++++++++++ |----(props)-> LikeButton
+ + |
(fetch) + |
+ + * ---(props)-> Description
++(setState)++
Widget -(props)-> Whether
However it gets more interesting when facebook's graphql is finalized and every component can declare the needed data on it's own, I'm looking forward to it. But until then the toplevel component has to know which data every child needs and all the parent nodes need to hand this data down.
Related
i am setting the language name in my local storage , when it changes from a dropdown in topbar , i want the whole current view to be re-rendered and words translated to the selected language. my layout is like this
render(){
return (
<MainContainer>
<TopBar/>
<SideBar/>
<RouteInsideSwitch/>
</MainContainer>
)
}
in render of components ,the words to be translated basically calls a function that returns the correct word based on the local storage language name.
i change the language and i set the state in maincontainer for selected langauge and set it in local storage. however i dont want to move that state from Maincontainer to all my components. also dont want to store it in redux because then all the possible containers have to listen to it and then pass it to their children as props.
what currently happens is that saving state in mainContainer without passing it to any children , the children does re-render but only the immediate ones , if there are more children in those children and so on , it does not re-render because i m not passing the state throughout the chain.
open to any suggestion based on different pattern for language changing. but my question is that is there any way to re-render the current open view (all components in dom).
If your concern is that you have a number of "possible containers" which all need to handle the state change, perhaps consider creating a higher order component that includes the common language rendering logic (your RouteInsideSwitch leads me to believe this may the issue). In that way, you can avoid duplicating that logic across a ton of "possible" components that all require the functionality of dynamic language rendering and will avoid the need to dial a bunch of components into a redux store, assuming they are in the same hierarchy.
const DynamicLanguageComp = RenderComponent => {
return class extends Component {
constructor(props) {
super(props)
//additional state setup if needed
}
changeLangFunc = () => { /* handle change */ }
render() {
return <RenderComponent handleLanguageChange={this.changeLangFunc} {...this.props} {...this.state} />
}
}
}
If you would like to avoid a re-render on certain intermediate components that may be receiving props by way of state change you can implement the lifecycle method shouldComponentUpdate(), which by default returns true. You can make a comparison of nextProps to your current props, and return false if a re-render is undesired despite new props.
We have a crazy DOM hierarchy, and we've been passing JSX in props rather than embedding children. We want the base class to manage which documents of children are shown, and which children are docked or affixed to the top of their associated document's window.
List (crazy physics writes inline styles to base class wrappers)
Custom Form (passes rows of JSX to Base class)
Base Class (connects to list)
Custom Form (passes rows of JSX to base class)
Base class (connects to list)
The problem is that we're passing deeply nested JSX, and state management / accessing refs in the form is a nightmare.
I don't want to re-declare every row each time, because those rows have additional state attached to them in the Base Class, and the Base Class needs to know which rows actually changed. This is pretty easy if I don't redeclare the rows.
I don't know how to actually deal with rows of JSX in Custom Form.
Refs can only be appended in a subroutine of render(). What if CustomForm wants to measure a JSX element or write inline CSS? How could that JSX element exist in CustomForm.state, but also have a ref? I could cloneElement and keep a virtual DOM (with refs) inside of CustomForm, or depend on the base class to feed the deeply-nested, mounted ref back.
I believe it's bad practice to write component state from existing state. If CustomForm state changes, and I want to change which rows are passed to BaseClass, I have to throttle with shouldComponentUpdate, re-declare that stage document (maintaining row object references), then call setState on the overarching collection. this.state.stages.content[3].jsx is the only thing that changed, but I have to iterate through every row in every stage document in BaseClass when it sees that props.stages changed.
Is there some trick to dealing with collections of JSX? Am I doing something wrong? This all seems overly-complicated, and I would rather not worsen the problem by following some anti-pattern.
Custom Form:
render () {
return <BaseClass stages={this.stages()}/>
}
stages () {
if (!this._stages) this._stages = { title: this.title(), content: this.content() };
return this._stages;
}
title () {
return [{
canBeDocked: false,
jsx: (
<div>A title document row</div>
)
}
}
content () {
return [{
canBeDocked: false,
jsx: (
<div>Hello World</div>
)
}, {
canBeDocked: true,
jsx: (
<div>Yay</div>
)
}
}
What I usually do is just connect the lower level components via Redux. This helps with not passing the state in huge chunks from the top-most component.
A great video course by one of the React creators, Dan Abramov: Getting started with Redux
Absolutely agree with #t1gor. The answer for us was to use REDUX. It changed the entire game for us. Suddenly a button that is nested 10 levels deep (that is, inside a main view, header, header-container, left side grid, etc, etc, deeper and deeper) into purely custom components, has a chance to grab state whenever it needs.
Instead of...
Parent (pass down state) - owns state vars
Child (will pass down again) - parent has state vars
Grandchild (will pass down a third time) - grandparent has state vars
Great Grandchild (needs that state var) - great grandparent has state vars
You can do...
Parent (no passing) - reads global state vars
Child
Grandchild
Great Grandchild - also reads same global level state vars without being passed...
Usually the code looks something like this...
'use strict'
//Importation of Connection Tools & View
import { connect } from 'react-redux';
import AppView from './AppView';
//Mapping -----------------------------------
const mapStateToProps = state => {
return {
someStateVar: state.something.capturedInState,
};
}
const mapDispatchToProps = dispatch => {
return {
customFunctionsYouCreate: () => {
//do something!
//In your view component, access this by calling this.props.customFunctionsYouCreate
},
};
}
//Send Mappings to View...
export default connect(mapStateToProps, mapDispatchToProps)(AppView);
Long story short, you can keep all global app state level items in something called a store and whenever even the tiniest component needs something from app state, it can get it as the view is being built instead of passing.
The issue is having content as follows, and for some reason not being able to effectively persist the child instances that haven't changed (without re-writing the entire templateForChild).
constructor (props) {
super(props);
// --- can't include refs --->
// --- not subroutine of render --->
this.state = {
templateForChild: [
<SomeComponentInstance className='hello' />,
<AnotherComponentInstance className='world' />,
],
};
}
componentDidMount () {
this.setState({
templateForChild: [ <div className='sometimes' /> ],
}); // no refs for additional managing in this class
}
render () {
return ( <OtherManagerComponent content={this.state.templateForChild} /> );
}
I believe the answer could be to include a ref callback function, rather than a string, as mentioned by Dan Abramov, though I'm not yet sure if React does still throw a warning. This would ensure that both CustomForm and BaseClass are assigned the same ref instance (when props.ref callback is executed)
The answer is to probably use a key or createFragment. An unrelated article that addresses a re-mounting problem. Not sure if the fragment still includes the same instances, but the article does read that way. This is likely a purpose of key, as opposed to ref, which is for finding a DOM node (albeit findDOMNode(ref) if !(ref instanceof HTMLElement).
I have a component ProductList - it's a parent component. In m render method i wrote such code
return (
<div>
<CustomBreadCrumbs routes={this.props.routes} params={this.props.params} />
{ this.props.children ? this.props.children :
<section className="content">
Parent
</section>
}
</div>
);
When I edit some info in child component my parent component rerender, but i want prevent it. How i can do it?
This is impossible, because only on rerendering parent component calling rerendering of the child.
As you can see there, if you will prevent rerendring of current element with shouldComponentUpdate, the childs render methods will not hired.
But dont worry React Only Updates What's Necessary. So, if your html of the parent element will not change, the real DOM will update only child`s html.
Show case
There is an example in official documentation, of how to create forms. In a few words, your main problem, is that you dont save your values anywhere, as I see, you use Redux and passing all of the data via props. Try to change your code, to save the data in the own state of the component.
And if you will catch an error on BadRequest, you will fire the code, check the equality, for example for message (of an error) and update your component, but your current state, with all user`s data will not be changed.
shouldComponentUpdate(nextProps, nextState) {
//there you will get the new values and check it, if they not equal
}
componentDidUpdate(prevProps, prevState) {
//there you can do anything with your new values
}
And if you r using Redux, take a look to Redux Form.
I'm working on a project using React.js, and very confused about the composition of React.
http://facebook.github.io/react/docs/multiple-components.html
the link above gives an example. It uses React.creatClass() create three components. A parent component and two child components. The parent component includes the others within it's JSX in the render method.
This example's very clear, but not very 'reusable'. What if I wanna pass in another child in another situation? React.js seems lacking the 'extend' method like Backbone's view.
later, I found that you can pass children components in the React.render(), and use this.props.children to composite.
var Tom = React.createClass({
render: function(){
return(
<a>This is Tom.</a>
)
}
});
var John = React.createClass({
render: function(){
return(
<a>This is John.</a>
)
}
});
var Outter = React.createClass({
componentDidMount:function(){
console.log(this.props.children);
},
render: function(){
return(
<div className="test">
{this.props.children}
</div>
)
}
});
React.render(<Outter><Tom /><John /></Outter>, document.getElementById('main'));
I think that's great but what's the really difference between this method and the example above? is this method the right way to composite components in React.js?
thanks
The difference is just like you said, that you can pass any components you'd like as children of that component. Components that use this.props.children are usually components that acts as wrappers for style and behaviour, but the contents of the component changes for different use cases. Like a popup, where you want the same look and behaviour (like a close button) for every popup, but the contents of the popup is different for every popup.
Components that don't use this.props.children are more like black boxes, they know everything about how they should be rendered and what child components they need. But you can still make them dynamic by passing other props to them.
Components can be also be passed as props. Components are just JS objects, and any JS object can be passed as a prop. But I don't think I've ever seen a use case for doing it. It might make some sense if you have a wrapper component with two or more specific "slots" that should be rendered to. Something like:
var Wrapper = require('./wrapper');
var Header = require('./title');
var Content = require('./content');
var Footer = require('./footer');
var MyComponent = React.createClass({
render() {
return <Wrapper footer={Footer} header={Header} content={Content} />;
}
});
It depends on your use-case, take this two examples of forms:
a login form - almost all login forms have the same structure, username/email, password, a keep me logged in checkbox, and a login button; a LoginForm component doesn't need to be further customised in regards to its structure so it's suitable to be used as is
a signup form - now depending on context you might give more or less fields to the user to complete when signing up, and in this case you use props.children
Why do we have both state and props? Why don't we just have one source of data? I'd like to update a component's props and have it re-render itself and all of its children. Seems simple but I can't figure out how to let a component update its own or its parent's props.
Thanks for any help.
The React philosophy is that props should be immutable and top-down. This means that a parent can send whatever prop values it likes to a child, but the child cannot modify its own props. What you do is react to the incoming props and then, if you want to, modify your child's state based on incoming props.
So you don't ever update your own props, or a parent's props. Ever. You only ever update your own state, and react to prop values you are given by parent.
If you want to have an action occur on a child which modifies something on the state, then what you do is pass a callback to the child which it can execute upon the given action. This callback can then modify the parent's state, which in turns can then send different props to the child on re-render.
To answer the question of why
In React, props flow downward, from parent to child.
This means that when we call ReactDOM.render, React can render the root node, pass down any props, and then forget about that node. It's done with. It's already rendered.
This happens at each component, we render it, then move on down the tree, depth-first.
If a component could mutate its props, we would be changing an object that is accessible to the parent node, even after the parent node had already rendered. This could cause all sorts of strange behaviour, for example, a user.name might have one value in one part of the app, and a different value in a different part, and it might update itself the next time a render is triggered.
To give a fictional example:
// App renders a user.name and a profile
const App = (props) =>
React.createElement('div', null, [
props.user.name,
React.createElement(Profile, props)
])
// Profile changes the user.name and renders it
// Now App has the wrong DOM.
const Profile = ({user}) => {
user.name = "Voldemort" // Uh oh!
return React.createElement('div', null, user.name);
}
// Render the App and give it props
ReactDOM.render(
React.createElement(App, {user: {name: "Hermione"}}),
document.getElementById('app'))
);
We render app. It outputs "Hermione" to the Shadow DOM. We render the Profile, it outputs "Voldemort". The App is now wrong. It should say "Voldemort" because user.name is "Voldemort", but we already output "Hermione", and it's too late to change it.
The value will be different in different parts of the app.
Modifying Props would be two-way-binding
Mutating props would be a form of two-way binding. We would be modifying values that might be relied on by another component higher up the tree.
Angular 1 had this, you could change any data anytime from wherever you were. In order to work, it needed a cyclical $digest. Basically, it would loop around and around, re-rendering the DOM, until all the data had finished propagating. This was part of the reason why Angular 1 was so slow.
In React, state and props serve different goals: state allows a component to maintain some changing values, while props are the mecanism to propagate those values to children.
Children are not allowed to alter by themselves the values they get via props just because React designers find it easier to maintain an application built this way. Their point is that when only one component is allowed to update some piece of state, it is easier to discover who altered it, and find the root of bugs.
the Component itself changes its state, and changes not its own, but the children's props.
<Parent>
<Child name={ this.state.childName } />
</Parent>
Parent can change its own state and change the child name, but it will change the props for his children.
edit1:
for calling events from the child to its parent, you should pass in the child an event handler like so:
var Child = React.createClass({
render: function() {
return (<button onClick={ this.props.onClick }>Hey</button>);
}
});
var Parent = React.createClass({
onChildClick: console.log.bind(console), // will print the event..
render: function() {
return (<Child onClick={ this.onChildClick } />);
}
});
React.renderComponent(<Parent />, document.body);
in this code, when you'll click on the Child's button, it will pass the event to its parent.
the purpose of passing the events is decoupling the components. maybe in your app you need this specific action, but in another app you'll have, you'll use it differently.
My solution was fairly different but some people might run into it. On the Chrome Dev tools, it kept saying that my props were read-only and when I tried passing them down even further, I would get an error. Now, the reason why is because I wasn't invoking a render() method. I was instead calling my component like this:
const Navigation = () =>{
return (
<div className="left-navigation">
<ul>
<Link to='/dashboard'><li>Home</li></Link>
<Link to='/create-seedz'><li>Create Seedz</li></Link>
<Link to='/create-promotion'><li>Create Promotion</li></Link>
<Link to='/setting'><li>Setting</li></Link>
<SignOutButton />
</ul>
</div>
);
}
I added a render method and it solved my issue of being able to pass props down:
class Navigation extends Component{
render(){
return (
<div className="left-navigation">
<ul>
<Link to='/dashboard'><li>Home</li></Link>
<Link to='/create-seedz'><li>Create Seedz</li></Link>
<Link to='/create-promotion'><li>Create Promotion</li></Link>
<Link to='/setting'><li>Setting</li></Link>
<SignOutButton user={this.props.user} signedOut={this.props.signedOut} authed={this.props.authed}/>
</ul>
</div>
);
}
}
Hopefully this helps someone.
Contrary to the answers provided here, you actually can update props directly, if you don't mind defying the pedantic circlejerk about "the React way." In React.js, find the following lines of code:
Object.freeze(element.props);
Object.freeze(element);
and comment them out. Voila, mutable props!