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>
Related
I'm new to React and have this simple code example where I simply need to take value from input and show the value back.
class App extends React.Component {
constructor(props){
super(props);
this.state = { word : ""};
this.onClick = this.onClick.bind(this);
}
onClick(e){
this.setState({word : /* how to obtain input value?? */});
}
render() {
return (
<>
<form>
<input type="text"/>
<button onClick={this.onClick}>Say it!</button>
</form>
<div>
{this.state.word}
</div>
</>
);
}
}
I know react want's me to use component state as a way to propagate information from parent component to it's children. What I don't know is how I should obtain state of a children to be used in another children.
I believe this should be doable in react in simple manner as the equivalent way of doing it using pure DOM or JQuery would also be very simple (one or two lines of code).
You can use createRef
import React, { createRef } from "react";
import "./styles.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = { word: "" };
this.onClick = this.onClick.bind(this);
}
textInput = createRef();
onClick(e) {
this.setState({ word: this.textInput.current.value });
}
render() {
return (
<div className="App">
<form>
<input ref={this.textInput} type="text" />
<button onClick={this.onClick} type="button">
Say it!
</button>
</form>
<div>{this.state.word}</div>
</div>
);
}
}
export default App;
check here CodeSandBox
A few things here. First I don't see children as you mention. Moreover, you say obtain state of a children to be used in another children. You have just one parent component here. Then, you are using a <form> which means a button inside will submit the values so you need the escape hatch of e.preventDefault(). Finally, if you must use a class based component instead of functional component, you don't need any more constructor and you can bind your functions with an arrow function. Here is a working example of what I presume you are asking: https://codesandbox.io/s/sleepy-minsky-giyhk
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>
I have two components each on separate routes. I would like to know how I can keep the DOM elements in the same state on route change. For example I would like for all the DOM elements to have the same css classes applied as before the route change when navigating back to the same component.
I have tried redux persist and using nested routes with switch but none of these seem to work. From the research I have done it appears that React always mounts and unmount the component on route change and I haven't' been able to find a way to prevent this happening.
I would like for the red background color to remain when going back to test1.
class test1 extends React.Component {
constructor(props) {
super(props);
}
addClassFucn = event => {
$(event.target).parent().css("background-color", "red")
}
renderButton() {
return (
<div>
<div>
<button onClick={this.addClassFucn}>Click me</button>
<Link to="/test2" className="ui button primary back" >
test2
</Link>
</div>
</div>
)
}
render() {
return (
<div>
<div>This is test 1{this.renderButton()}</div>
</div>
);
}
}
export default test1;
class test2 extends React.Component {
constructor(props) {
super(props);
}
renderButton() {
return (
<div>
<Link to="/test1" className="ui button primary back" >
back
</Link>
</div>
)
}
render() {
return (
<div>
<div>This is test 2{this.renderButton()}</div>
</div>
);
}
}
export default test2;
It really depends on the logic of how you maintain the state of your component.
Redux persist should work. Just persist all the state that affects how the DOM currently displayed. Afterwards, inside the component, you should do a check whether there is a persisted state or not. If it is then you shouldn't do any change and just render.
You can use react's shouldComponentUpdate() which by default returns true allowing the component to re render. If useful than you can add the logic to return false which won't all the component to re-render. This is not recognized as best practice though you can refer this link for more details.
as the link https://facebook.github.io/react/docs/more-about-refs.html#the-ref-callback-attribute
It then only gives an example of using the component immediately. I'm trying to find out how i would use this function to access the component immediately, and save the component for future use, as it says we are able to do.
The difference is using ref={callback} react passes the responsibility of managing the reference storage back to you. When you use ref="sometext", under the covers react has to create a refs property on your class and then add all the ref="sometext" statements to it.
While its nice to have a simple this.refs.sometext access to components its difficult and error prone on the react side to clean up this refs property when the component is destroyed. It's much easier for react to pass you the component and let you handle storing it or not.
According to the react docs
React will call the ref callback with the DOM element when the
component mounts, and call it with null when it unmounts.
This is actually a pretty slick idea, by passing null on unmount and calling your callback again you automatically clean up references.
To actually use it all you have to do is access it from any function like so:
class CustomTextInput extends React.Component {
constructor(props) {
super(props);
this.focus = this.focus.bind(this);
}
focus() {
// Explicitly focus the text input using the raw DOM API
this.textInput.focus();
}
render() {
// Use the `ref` callback to store a reference to the text input DOM
// element in this.textInput.
return (
<div>
<input
type="text"
ref={(input) => { this.textInput = input; }} />
<input
type="button"
value="Focus the text input"
onClick={this.focus}
/>
</div>
);
}
}
The callback you set on ref will receive the component as the first parameter, the 'this' word will be the current class 'CustomTextInput' in this example. Setting this.textInput in your callback will make textInput available to all other functions like focus()
Concrete Example
Tweet from Dan Abermov showing a case where ref callbacks work better
Update
Per Facebook Docs using strings for refs is consider legacy and they "recommend using either the callback pattern or the createRef API instead."
When you assign a ref={callback} like <input type="text" ref={(input) => {this.textInput = input}}/> what basically you are doing is saving the ref with the name textInput for future use. So instead of using ref="myInput" and then using this.refs.myInput we can use the call back bethod and then access the component later like this.textInput.
Here is a demo for the same, whereby we are accessing the input value using ref on button click
class App extends React.Component {
constructor(){
super();
}
handleClick = () => {
console.log(this.textInput.value);
}
render() {
return (
<div>
<input type="text" ref={(input) => {this.textInput = input}}/>
<button type="button" onClick={this.handleClick}>Click</button>
</div>
)
}
}
ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.4/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.4/react-dom.min.js"></script>
<div id="app"></div>
With React 16.3, you can use React.createRef() API instead:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return <div ref={this.myRef} />;
}
}
I am following a tutorial to understand how React.js works. I am stuck where using refs to access components. If anyone don't mind help look at my code and tell me where I am wrong. But, this is what I did. I have a general component App and html Input tag that update the refs in the State. But, I don't know why it's still showing undefined
import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component {
constructor() {
super();
this.state = {
red: 0,
green: 0,
blue: 0
}
this.update = this.update.bind(this);
}
//To manage the state
update(e) {
this.setState({
red: ReactDOM.findDOMNode(this.refs.red.refs.inp).value,
green: ReactDOM.findDOMNode(this.refs.green.refs.inp).value,
blue: ReactDOM.findDOMNode(this.refs.blue.refs.inp).value
})
console.log(this.state); //<This is giving me Object {red: undefined, green: undefined, blue: undefined}
}
render() {
return (
<div>
<Slider ref="red" update={this.update}/>
{console.log(this.state.red)} // This is undefined
{this.state.red}
<br />
<Slider ref="green" update={this.update}/>
{console.log(this.state.green)} // This is undefined
{this.state.green}
<br />
<Slider ref="blue" update={this.update}/>
{console.log(this.state.blue)} // This is undefined
{this.state.blue}
<br />
</div>
)
}
}
//Working with refs. Refs are a way of referencing components within our react application
class Slider extends React.Component {
render() {
return (
<div>
<input ref="inp" type="range" min="0" max ="255" onChange={this.props.update}/>
</div>
)
}
}
ReactDOM.render(
<App />, document.getElementById('app')
);
export default App;
According to React doc :
If you have not programmed several apps with React, your first inclination is usually going to be to try to use refs to "make things happen" in your app. If this is the case, take a moment and think more critically about where state should be owned in the component hierarchy. Often, it becomes clear that the proper place to "own" that state is at a higher level in the hierarchy. Placing the state there often eliminates any desire to use refs to "make things happen" – instead, the data flow will usually accomplish your goal.
Try this :
class App extends Component {
constructor(){
super();
this.state = {
slider : 0,
}
this.update = (e) => {
const {name,value} = e.target;
this.setState({
[name] : value,
})
}
}
render() {
return (
<div>
<Slider name="slider" update={this.update} />
{this.state.slider}
</div>
);
}
}
class Slider extends React.Component {
render() {
return (
<div>
<input name={this.props.name} type="range" min="0" max="255" onChange={this.props.update}/>
</div>
)
}
}
findDOMNode returns the corresponding native browser DOM element, which is in your case:
<div>
<input type="range" min="0" max ="255" onChange={this.props.update} />
</div>
So you are reading the value of the div instead of the input.
Check this fix http://jsfiddle.net/69r5kn16/2/
if you want to use refs, you should use them after your component have been mounted, it means you should use them in componentDidMount.
I see you call this.update in your Slider Component. the order componentDidMount of child component and parentComponent is as follow:
componentWillMount Of parent
componentWillMount of child
componentDidMount of child
componentDidMount of Parent.
please check when do you call update method in your Slider Component.