As my application grows more and more there are more discussion in my head about managing the component state. I have a big parent component which include a few child components so each of them render specific set of data.Until now in other components I was strictly updating the parent state only and not duplicating the state at all as I read in the docs and etc.. As now my children render a few inputs each and updates the parent state object. Question - Is that correct or I should duplicate some of the parent state in my children so the inputs use them directly and onBlur action they update the parent? The benefit is that it will render the parent only 1 time onBlur as it is at the moment updating directly the parent state it is rendered on each user input.
I tried both cases and the performance using the react profiler is more for the duplicating state idea -only because the parent is rendering only once not on each user onchange input. That's why I want to ask if it's something wrong. Each other small component which is part of the big one is a PureComponent and don't do useless rendering.
class TestParent extends React.Component {
constructor(props) {
super(props);
this.state = {
testObject: {
...many properties
}
};
}
onChange = (evt) => {
thi.setState({
testObject: { ...testObject, [evt.target.id]: evt.target.value
})
}
render(){
<ChildComponent1 onChange={this.onChange} prop1={state.testObject.value1} prop2={state.testObject.value2}
prop3={state.testObject.value3}> </ChildComponent1>
<ChildComponent2 onChange={this.onChange} prop4={state.testObject.value4}> </ChildComponent2>
<ChildComponent3 onChange={this.onChange} prop5={state.testObject.value5}> </ChildComponent3>
<ChildComponent6 onChange={this.onChange} prop6={state.testObject.value6}> </ChildComponent4>
<ChildComponent7 onChange={this.onChange} prop7={state.testObject.value7}> </ChildComponent7>
<textbox value={this.state.testObject} />
}
class ChildComponent1 extends React.PureComponent {
constructor(props) {
super(props);
}
}
render(){
<Input onChange={this.props.onChange} value={this.props.prop1}</Input>
<Input onChange={this.props.onChange} value={this.props.prop2}> </Input>
<Input onChange={this.props.onChange} value={this.props.prop3}> </Input>
}
To answer your question: Whether you should take a prop passed to a child and add it to state on componentDidMount then update parent state onBlur or onSubmit within the child component?
Best practice would be to only pass state from parent component to child component that is either shared by children of the parent or is used by the parent itself.
In your example it would be best to manage the input value state within the child component then to use a callback function to setState on the parent component on blur.
Simplified example:
class Parent extends React.Component {
state = {
child1State:'' // initializing state is not strictly necessary
};
changeParentState = (value)=>{
this.setState(value)
}
render(){
return(
<div>
<Child1Component
changeParentState={this.changeParentState}
/>
{this.state.child1State}
</div>
)
}
}
class Child1Component extends React.PureComponent {
state={
inputValue:''
}
handleBlur=()=>{
this.props.changeParentState({child1State : this.state.inputValue})
}
render(){
return (
<input
onChange={(e)=> this.setState({inputValue:e.target.value})}
value={this.state.inputValue}
onBlur={this.handleBlur}
/>
)
}
}
ReactDOM.render(
<Parent />,
document.getElementById("react")
);
<div id='react'></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Related
I'm new to React and still learning.
I'm trying to pass data from child to grandparent. So far I reach to parent and I`m stuck.
Child component:
export class Child extends React.Component{
constructor(props) {
super(props);
this.state= {
counterChild: 5
}
}
render() {
return(
<div>
<span>Child: {this.state.counterChild}</span><br />
<button onClick={this.props.data(this.state.counterChild)}>Click me</button>
</div>
);
}
}
Parent component:
export default class Parent extends React.Component{
constructor(props){
super(props);
this.state= {
counterParent: 0
}
}
updateParent(value) {
return() => {
this.setState({
counterParent: value
});
}
}
componentWillMount(){
this.props.data(this.state.counterParent)
}
render(){
return(
<div>
<span>Parent: {this.state.counterParent}</span>
<Child data={this.updateParent.bind(this)}/>
</div>
);
}
}
in child component i use a button
and here i guess i have to use componentWillMount in order to send to grandparent..but it does not reach
Grandparent component:
export default class Grandparent extends React.Component{
constructor(props){
super(props);
this.state = {
counterGrandparent: 0
}
}
updateGrandparent(value){
return() => {
this.setState({
counterGrandparent: value
});
}
}
render(){
return(
<div>
<span>Grandparent: {this.state.counterGrandparent}</span>
<Parent data={this.updateGrandparent.bind(this)}/>
</div>
);
}
}
What did I do wrong here?
As you may have figured out, data is passed down the component tree in the form of props, and up in the form of prop callback functions. When something happens in a child, you call the callback to inform the parent. The parent then updates its state and passes the new state to the child as a prop.
In your case, you have three nested components, each with their own state. Typically, only a parent "container" component will have state, and child components will be stateless. So let's remove the state from the Child and Parent components. The Child component interacts with the user with a button, so whenever the button is pushed, the event handler is called and the data flows up the tree using callbacks. I added some borders and padding to make the nesting clear:
Part of the problem lies in the onClick handler on your button. Event handlers should be function references, but you have used a function call. So your child might be like below. Note the counter prop that receives the current state, and the updateParent prop that allows the Child to update the Parent.
import React from 'react';
const boxStyle = {
border: '1px solid red',
padding: '5px'
};
export class ChildWithButton extends React.Component {
handleClick(event) {
this.props.updateParent(this.props.counter + 1);
}
render() {
return(
<div style={boxStyle}>
<div>Child: {this.props.counter}</div>
<button onClick={this.handleClick.bind(this)}>
Add 1
</button>
</div>
);
}
}
The Parent component passes the current state down in the counter prop, and lets the Child component change the state by calling the updateParent callback that it received as a prop:
export class Parent extends React.Component{
updateParent(value) {
this.props.updateGrandparent(value);
}
render() {
return(
<div style={boxStyle}>
<div>Parent: {this.props.counter}</div>
<ChildWithButton
counter={this.props.counter}
updateParent={this.updateParent.bind(this)} />
</div>
);
}
}
The Grandparent component holds the state, passing it down to the Parent in counter and allowing it to update it with updateGrandparent. It should be noted that Grandparent has no knowledge of Child, only of Parent.
export default class Grandparent extends React.Component {
constructor(props) {
super(props);
this.state = {counter: 5};
}
updateGrandparent(value){
this.setState({counter: value});
}
render() {
return (
<div style={boxStyle}>
<div>Grandparent: {this.state.counter}</div>
<Parent
counter={this.state.counter}
updateGrandparent={this.updateGrandparent.bind(this)} />
</div>
);
}
}
You should avoid using componentWillMount as it will be removed in a future version of React.
You should also name the function you pass down in props something other than data. Function names are typically verbs.
There was more than one thing wrong with your code, so I hope this answers your question.
Let's consider the following sample:
import React, {Component} from 'react';
class B extends Component {
render() {
console.log(`Render runs with ${this.props.paramA}`);
return (<div>{this.props.paramA}</div> );
}
}
class App extends Component {
constructor() {
super();
this.state = {paramA: 'asd'};
}
handleChange(event) {
this.setState({paramA: event.target.value});
}
render() {
return (<div>
<input value={this.state.paramA} onChange={e => this.handleChange(e)}/>
<label>
<B paramA={this.state.paramA}></B>
</label>
</div>);
}
}
Here's the gif of how it works.
If you noted, in order to update the changes from properties, react needs to evaluate "render" method. That causes the whole component to update instead of its small part that really changed (check the gif, the div element blinks in chrome developer tools):
TL;DR According to react philosophy,apps should be written in a way to have as many dummy components as possible. That means we have to pass properties a few level down sometimes (other time we can use e.g. redux), which leads to a lot of render methods that evaluate every time the property of top level component changes. With all that being said I often see in the real life react application that a whole root div blinks when e.g. users types something into input. Well even if it's a browser "lag" I don't really like the idea that react reevaluates all components (meaning running their render method) when a component needs to update only its small part.
The question:
Am I doing something wrong? Is there a way to implement react component so they update only things that changed?
It sounds like you're looking for the shouldComponentUpdate lifecycle hook.
Pretty self explanatory; if the component should only re-render under specific prop/state changes, you can specify those in this hook, and return false otherwise.
In this case, React is not rerendering the entire component but the first parent of the dynamic part of them. In this case, the <div> is the parent (and the entire component so you're right), but in this fiddle wrapping {this.props.paramA} inside a paragraph tag, the <div> is not the direct parent, so just rerenders <p> tag and <div> does not need to update.
class B extends React.Component {
render() {
console.log(`Render runs with ${this.props.paramA}`);
return (<div><p>{this.props.paramA}</p></div> );
}
}
class App extends React.Component {
constructor() {
super();
this.state = {paramA: 'asd'};
}
handleChange(event) {
this.setState({paramA: event.target.value});
}
render() {
return (<div>
<input value={this.state.paramA} onChange={e => this.handleChange.bind(this)(e)}/>
<label>
<B paramA={this.state.paramA} />
</label>
</div>);
}
};
ReactDOM.render(
<App />,
document.getElementById('container')
);
<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>
<div id="container"></div>
I want to render BlackSpark when RedSpark is clicked, but I'm not sure how to change the state of a component in another component. I know how to set state in the component itself, but how do I affect another component when I click a different component?
class BlackSpark extends React.Component {
render() {
return (
<div className="black"></div>
);
}
}
class RedSpark extends React.Component {
render() {
return (
<div className="red"></div>
);
}
}
class App extends React.Component {
render() {
return (
<div>
<BlackSpark />
<RedSpark />
</div>
);
}
}
In React, there's a concept of component composition as you've already embraced -- it allows you to accomplish what you want by rendering children based on the parent's state, another key concept known as lifting state up. What this means, is if you have mutually dependent components, create a single parent which composes them, and have state in the parent control the presentation and logic of the children. With the parent App, you can keep your state inside App, and based on App's state, conditionally render whatever you want -- either BlackSpark or both. For example, using the logical && operator:
{condition && <Component />}
This will only render <Component> when condition is truthy, or else it will not render anything at all (except for when condition is 0). Applying it to this situation, try adding state to your App component to utilize conditional rendering.
There's another key concept you need to understand: component props. They are essentially inputs to a component, certain properties passed to the component to tell how it should behave -- like attributes on regular HTML elements such as input placeholders, URLs, and event handlers. For example:
<Component foo="bar" bar={3} />
This will pass the props foo and bar down to Component with the values "bar" and 3 respectively and are accessible through this.props. If you were to access this.props.foo inside the Component component it would give you "bar". If you pair this up with composition, you can accomplish what you want:
class Example extends React.Component {
constructor() {
super();
this.state = {
showHello: true
}
this.handleChange = this.handleChange.bind(this);
}
handleChange() {
this.setState(prevState => ({
showHello: !prevState.showHello
}));
}
render() {
return (
<div>
{this.state.showHello && <Child2 />}
This is a test.
<Child1 onClick={this.handleChange} />
</div>
);
}
}
class Child1 extends React.Component {
render() {
return <div onClick={this.props.onClick}>Click me!</div>
}
}
class Child2 extends React.Component {
render() {
return <div>Hello!</div>
}
}
ReactDOM.render(<Example />, 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>
<div id="root"></div>
The above example lifts state up by having a parent compose the children and maintain the state. It then uses props to pass down an onClick handler to Child1, so that whenever Child1 is clicked, the state of the parent changes. Once the state of the parent changes, it will use conditional rendering to render <Child2> if the condition is truthy. Further reading at the React documentation and on the logical && operator.
I know how to set state in the component itself, but how do I affect another component when I click a different component?
The recommended way to do it would be to create a parent component that has the state. You'd then use that state to determine when to render the other child component.
I want to render BlackSpark when RedSpark is clicked, but I'm not sure how to change the state of a component in another component. Also, what if I want to hide BlackSpark when GreenSpark is clicked and GreenSpark is inside BlackSpark?
In this case, here's how you'd do it.
const GreenSpark = ({ onClick }) => (
<button className="green" onClick={onClick}>X</button>
)
const BlackSpark = ({ onClick }) => (
<div className="black">
<GreenSpark onClick={onClick} />
</div>
)
const RedSpark = ({ onClick }) => (
<div className="red" onClick={onClick}></div>
)
class Spark extends React.Component {
constructor(props) {
super(props)
this.state = {
showBlack: false
}
this.boundShowBlack = this.showBlack.bind(this)
this.boundHideBlack = this.hideBlack.bind(this)
}
showBlack() {
this.setState({ showBlack: true })
}
hideBlack() {
this.setState({ showBlack: false })
}
render() {
return (
<div>
<RedSpark onClick={this.boundShowBlack} />
{this.state.showBlack && <BlackSpark onClick={this.boundHideBlack} />}
</div>
)
}
}
My structure is as such:
<ParentComponent />
<Child 1 /> <Child 2 />
I have a function in <Child 1 />. Since <Child 1 /> controls the grid-layout, and <Child 2 /> is a Navbar with buttons, I want to have a button in <Child 2 /> which will reset some parameters in <Child 1 />.
How do I achieve this? As far as I can see, refs will only be able to go one "step" up the tree, not down.
There isn't any obvious way this function can be invoked from the parent component. Is there any way here? All the solutions I can think of aren't really following the React-mindset, namely the unidirectional data-flow.
You can make the parent component as a container for both components. So all the states and functions are handled in the parent components and pass them to the other components as props.
e.g.
class Parent extends React.Component {
constructor() {
super();
this.state = {
controls: controls
}
}
onClick = (dataFromChild2) => {
//Resetting
this.setState({controls: dataFromChild2})
}
render() {
return (
<div>
<Child1 gridControl={this.state.controls}/>
<Child2 onClick={this.onClick}/>
</div>
)
}
}
You can access the gridControl and onClick from this.props in the children components
UPDATE
Think of it this way, you have the Parent component with the states and function needed to handle the data. The children components take those data and update their states accordingly.
Let's Say the Parent Component is something like this:
class Parent extends React.Component {
constructor() {
super();
this.state = {
gridControl: {}
}
}
onChild2ButtonClick = (dataFromChild2) => {
this.setState({
gridControl: dataFromChild2
});
};
render() {
return (
<div>
<Child1 controls={this.state.gridControl}/>
<Child2 onClick={this.onChild2ButtonClick}/>
</div>
);
}
}
Child2 Component is something like this:
class Child2 extends React.Component {
constructor() {
super();
}
onClick = () => {
var data = {};
this.props.onClick(data);
};
render() {
return (
<div>
<button onClick={this.onClick}/>
</div>
);
}
If you're using states for Child1, and don't want to change them to props with function in the Parent component to handle them, then you update the state in the componentWillReceivePropsmethod with the new props received from the parent component, so that the props sent will match the states used in Child1 component.
Hope this will clear things up.
If your structure looks something as the following:
<ParentComponent>
<div>
<Child1 />
<Child2 />
</div>
<ParentComponent />
Both Child1 and Child2 should "communicate" through ParentComponent.
if Child2 will notify ParentComponent about a button click event then it can re-render Child1 with appropiate props accordingly or fire a function that Child1 gets as a prop. this is a basic flow in react.js
As an example consider a House component that has a Button and a Door child components. the Button will toggle opening and closing of the Door.
class House extends Component {
constructor(props) {
super(props);
this.state = {
isOpened: props.isOpened,
};
this.toggleOpenDoor = this.toggleOpenDoor.bind(this);
}
toggleOpenDoor() {
this.setState({
isOpened: !this.state.isOpened
});
}
render() {
return (
<div>
<Button onClick={this.toggleOpenDoor} />
<Door isOpened={this.state.isOpened} />
</div >
);
}
}
House.propTypes = {
isOpened: React.PropTypes.bool
};
House.defaultProps = {
isOpened: true
};
export default House;
On each change of this.state.isOpened the Door will re-render with the new value as prop
METHOD 1:
For this you need you need to maintain a store. On clicking button in your <Child2 /> component update the variable in the store. Read the updated variable in the store and if it has change update values in your <Child1 /> component. You can user either flux, redux, mobx etc.. as the store choices, but I would say you can start with redux.
METHOD2:
If you don't want to use store, keep a state in your <Parent /> and on button click in <Child2 /> update your state in parent through a callback function. Pass this state value as props to <Child1 /> and make changes if the prop is present.
I have two components one is app component and other one is sidebar component i have been using input field in side bar and i want to get the value of that input field in my app component on click how this could be possible ?
You can try lifting the state up.
Create a new component that will contain your two components. In that new component, create a function that you will pass as props inside the sidebar component.
class ContainerComponent extends React.Component {
constructor() {
super();
this.state = {
valueThatNeedsToBeShared: ''
}
}
handleChange(e) {
this.setState({valueThatNeedsToBeShared: e.target.value})
}
render() {
return (
<div>
<AppComponent value={this.state.valueThatNeedsToBeShared} />
<SidebarComponent handleChange={this.handleClick.bind(this)} value={this.state.valueThatNeedsToBeShared} />
</div>
)
}
}
const SidebarComponent = ({handleChange, value}) => <aside>
<input value={value} onChange={handleChange} />
</aside>
const AppComponent = ({value}) => <div>
value from sidebar: {value}
</div>
In pure react it is possible by adding callback from one component and use it into parent component to change their state and then send input value from parent's state to props of your second component