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.
Related
I am trying to have a controlled input set up in a child component (the Search component). I wanted to keep the input state in the main App component so that I can access it in my apiCall method. I am getting the following error:
Warning: You provided a value prop to a form field without an onChange handler. This will render a read-only field. If the field should be mutable use defaultValue. Otherwise, set either onChange or readOnly.
However, I did add an onChange handler. I'm assuming the problem is that the onChange handler function is in the parent component and React doesn't like this. I did try moving the input to the main App component and worked fine (logged input to console).
Am I going about this wrong? And is there a way to set it up so that I can access the input from the Search component in the App component? I was hoping to keep most of my code/functions/state in the main App component.
Here is the App component
import React, { Component } from "react";
import './App.css';
import Header from './Components/Header'
import Search from './Components/Search'
import MainInfo from './Components/MainInfo'
import Details from './Components/Details'
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
weather: null,
main: '',
wind: '',
loading: null,
cityInput: 'Houston',
city: 'City Name',
date: new Date()
};
this.apiCall = this.apiCall.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({
cityInput: event.target.value
})
console.log(this.state.cityInput)
}
// Fetch data from OpenWeatherAPI
apiCall() {
this.setState({
loading: true
})
const currentWeather = fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${this.state.cityInput}&appid={apiKey}&units=imperial`
).then((res) => res.json());
const futureWeather = fetch(
`https://api.openweathermap.org/data/2.5/forecast?q=houston&appid={apiKey}&units=imperial`
).then((res) => res.json());
const allData = Promise.all([currentWeather, futureWeather]);
// attach then() handler to the allData Promise
allData.then((res) => {
this.setState({
weather: res[0].weather,
main: res[0].main,
wind: res[0].wind,
city: res[0].name
})
});
}
componentDidMount() {
this.apiCall();
}
render() {
return (
<div className="container-fluid bg-primary vh-100 vw-100 d-flex flex-column align-items-center justify-content-around p-3">
<Header />
<Search cityInput={this.state.cityInput} />
<MainInfo main={this.state.main} date={this.state.date} city={this.state.city} weather={this.state.weather} />
<Details main={this.state.main} wind={this.state.wind} />
</div>
);
}
}
export default App;
Here is Search component
import React, { Component } from "react";
class Search extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="row">
<div className="col-12">
<div className="d-flex">
<input className="form-control shadow-none mx-1" placeholder="Enter a city..." value={this.props.cityInput} onChange={this.handleChange}></input>
<button className="btn btn-light shadow-none mx-1" onClick={this.apiCall}>Test</button></div>
</div>
</div>
);
}
}
export default Search;
The Search component is indeed unaware of the implementation of the onChange function you have made in your App. If you really want to use a function from the parent (App) component in the child (Search), you'll need to add it as a property, as such:
<Search cityInput={this.state.cityInput} onChange={this.onChange} />
Then, you need to set it in the Child component's constructor:
constructor(props) {
super(props);
this.onChange = props.onChange;
}
I also suggest you'll have a look at React's functional approach with hooks https://reactjs.org/docs/hooks-intro.html, which makes all this a whole lot less fiddly, in my opinion. But it might take a bit to get used to.
u can pass functions like ur handler over the prop to childrens and update so from a child to the mother of the children, in the children u give the value the prop u supply from mother
<Select dataFeld="broker" id="yourid" value={this.state.brokerSel} ownonChange={(e) => this.setState({statename: e})
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>
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'm currently trying to refactor the simple-todos tutorial for meteor using presentational and container components, but ran into a problem trying to access the refs of an input in a functional stateless component. I found out that to access refs, you have to wrap the component in a stateful component, which I did with the input.
// import React ...
import Input from './Input.jsx';
const AppWrapper = (props) => {
// ... more lines of code
<form className="new-task" onSubmit={props.handleSubmit}>
<Input />
</form>
}
import React, { Component } from 'react';
This Input should be stateful because it uses class syntax, at least I think.
export default class Input extends Component {
render() {
return (
<input
type="text"
ref="textInput"
placeholder="Type here to add more todos"
/>
)
}
}
I use refs to search for the input's value in the encompassing AppContainer.
import AppWrapper from '../ui/AppWrapper.js';
handleSubmit = (event) => {
event.preventDefault();
// find the text field via the React ref
console.log(ReactDOM.findDOMNode(this.refs.textInput));
const text = ReactDOM.findDOMNode(this.refs.textInput).value.trim();
...
}
The result of the console.log is null, so is my Input component not stateful? Do I need to set a constructor that sets a value for this.state to make this component stateful, or should I just give up on using functional stateless components when I need to use refs?
or should I just give up on using functional stateless components when I need to use refs?
Yes. If components need to keep references to the elements they render, they are stateful.
Refs can be set with a "callback" function like so:
export default class Input extends Component {
render() {
// the ref is now accessable as this.textInput
alert(this.textInput.value)
return (
<input
type="text"
ref={node => this.textInput = node}
placeholder="Type here to add more todos"
/>
)
}
}
You have to use stateful components when using refs. In your handleSubmit event, you're calling 'this.refs' when the field is in a separate component.
To use refs, you add a ref to where you render AppWrapper, and AppWrapper itself must be stateful.
Here's an example:
AppWrapper - This is your form
class AppWrapper extends React.Component {
render() {
return (
<form
ref={f => this._form = f}
onSubmit={this.props.handleSubmit}>
<Input
name="textInput"
placeholder="Type here to add more todos" />
</form>
);
}
};
Input - This is a reusable textbox component
const Input = (props) => (
<input
type="text"
name={props.name}
className="textbox"
placeholder={props.placeholder}
/>
);
App - This is the container component
class App extends React.Component {
constructor() {
super();
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
event.preventDefault();
const text = this._wrapperComponent._form.textInput.value;
console.log(text);
}
render() {
return (
<AppWrapper
handleSubmit={this.handleSubmit}
ref={r => this._wrapperComponent = r}
/>
);
}
}
http://codepen.io/jzmmm/pen/BzAqbk?editors=0011
As you can see, the Input component is stateless, and AppWrapper is stateful. You can now avoid using ReactDOM.findDOMNode, and directly access textInput. The input must have a name attribute to be referenced.
You could simplify this by moving the <form> tag into the App component. This will eliminate one ref.