I have the following react class. In it I'm using other react components. I'm new to React so I think I'm misunderstanding how state scope works. When menuClicked() is called, the outermost div's class will change but the innermost div's class does not. Can someone explain why?
import React, {Component} from 'react';
import Row from '../components/grid/Row.js'
import Cell from '../components/grid/Cell.js'
export default class HeaderBar extends Component {
state = {
menuOpen: false
};
constructor(props) {
super(props);
this.menuClicked = this.menuClicked.bind(this);
}
menuClicked() {
this.setState({menuOpen: !this.state.menuOpen})
};
render() {
return (
<div
className={`header-wrap ${this.state.menuOpen ? 'open' : ''}`}
>
<Row>
<Cell
c={1}
mc={12}
>
<div className={`platform-name ${this.state.menuOpen ? 'open' : ''}`}>The Platform Name</div>
</Cell>
<Cell>
<div onClick={this.menuClicked}></div>
</Cell>
</Row>
</div>
)
}
}
Added by popular demand
import React, {Component} from 'react';
export default class Row extends Component {
constructor(props) {
super();
this.children = props.children;
}
render() {
return (
<div className="r">
{this.children}
</div>
)
}
}
And the Cell class
import React, {Component} from 'react';
export default class Cell extends Component {
constructor(props) {
super();
this.props = props;
}
render() {
return (
<div
className={`c${this.props.c}`}
>
{this.props.children}
</div>
)
}
}
Problem in Row component constructor, you do this.children = props.children;, then you render this.children.
Remember constructor is called only once. So this.children is assigned once with initial value and never get updated afterwards. It’s a stale reference!
Don’t do that, just render this.props.children
Related
2 components :- ClickCounter, mouseHoverCounter !
1 HOC component to do the counting work.
earlier I was counting the click and mouse hover by writing separate counter method in each component(cliccounter,mousehovecounter),
but
now, I'm trying to pass the component into hoc counter & get the new component with only one change , where I'm passing a props to originalComponent and returning it to see the behavior but its now working...
import React, { Component } from 'react'
import updatedComponent from './hocCounter'
class ClickCounter extends Component {
constructor(props) {
super(props)
this.state = {
counter:0
}
}
ClickCounterHandler = () =>{
this.setState((prevState)=>{
return {counter:prevState.counter+1}
})
}
render() {
const count=this.state.counter
return (
<div>
<button onClick={this.ClickCounterHandler}>{this.props.name} Clicked {count} Times</button>
</div>
)
}
}
export default updatedComponent(ClickCounter)
import React, { Component } from 'react'
import updatedComponent from './hocCounter'
class HoverMouseCounter extends Component {
constructor(props) {
super(props)
this.state = {
counter:0
}
}
MouseOverCounter(){
this.setState((prevState)=>{
return {counter:prevState.counter+1}
})
}
render() {
const count=this.state.counter
return (
<div>
<h1 onMouseOver={this.MouseOverCounter.bind(this)}>{this.props.name} Hovered For {count} Time(s)</h1>
</div>
)
}
}
export default updatedComponent(HoverMouseCounter)
import React from 'react'
const updatedComponent = originalComponent => {
class newComponent extends React.Component {
render(){
return <originalComponent name='Harsh'/>
}
}
return newComponent
}
export default updatedComponent
In App.js, I'm returning
<ClickCounter></ClickCounter>
<HoverMouseCounter></HoverMouseCounter>
this only !
Check the error in the console,
index.js:1 Warning: <originalComponent /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements. at originalComponent
This means You are using the small letter in originalComponent
React components are expected to start with a capital letter
Try this in you HOC component
import React from 'react'
const updatedComponent = OriginalComponent => {
class NewComponent extends React.Component {
render(){
return <OriginalComponent name='Harsh'/>
}
}
return NewComponent
}
export default updatedComponent
so ,i have hard coded the state of the parent component and after passing it in the child component i am unable to retrieve it the child component.
and on the other hand if i pass any other other prop other than the state it works.
this is the child component:
import React from 'react';
import './searchresults.css'
class SearchResults extends React.Component {
render(){
let searchresults= this.props.searchresults;
return(
<div className= "searchresults">
<h2>Weather</h2>
<h3>Temprature:{searchresults.main.temp} </h3>
<h3>Temperature minimum: 25 degrees</h3>
<h3>Temperature maximum: 40 degrees</h3>
<h3>Humidity: 81% </h3>
</div>
)
}
}
export default SearchResults;
this is the parent component:
import React from 'react';
import './App.css';
import SearchBar from '../searchbar/searchbar'
import SearchResults from '../SearchResults/searchresults'
class App extends React.Component {
constructor(props){
super(props)
this.state = {
searchresults:[{"coord":{"lon":-0.13,"lat":51.51},"weather":[{"id":300,"main":"Drizzle","description":"light intensity drizzle","icon":"09d"}],"base":"stations",
"main":{"temp":280.32,"pressure":1012,"humidity":81,"temp_min":279.15,"temp_max":281.15
},"visibility":10000,"wind":{"speed":4.1,"deg":80},"clouds":{"all":90},"dt":1485789600,"sys":{"type":1,"id":5091,"message":0.0103,"country":"GB","sunrise":1485762037,"sunset":1485794875},"id":2643743,"name":"London","cod":200}]
}
}
render(){
return (
<div className="App">
<header className="App-header">
<h1>Wanderer</h1>
</header>
<SearchBar />
<SearchResults searchresults ={this.state.searchresults}
/>
</div>
);
}
}
export default App;
and this the error i get:
TypeError: Cannot read property 'temp' of undefined
SearchResults.render
F:/rishit/wanderer/src/components/SearchResults/searchresults.js:18
Since searchresults in the parent with one item you could pass it like :
<SearchResults searchresults ={this.state.searchresults[0]} />
searchResults is an arrray it should be searchresults[0].main.temp
<h3>Temprature:{searchresults[0].main.temp} </h3>
Here, searchresults is an array. You need to provide index to access an element.
You can use searchresults[0].main.temp but that is not the best way and not a solution if searchresults had multiple elements. I would do it in the following way.
import React from 'react';
import './searchresults.css'
class SearchResults extends React.Component {
render(){
return(
this.props.searchresults.map((searchResult) => (
<div className= "searchresults">
<h2>Weather</h2>
<h3>Temprature:{searchResult.main.temp}</h3>
<h3>Temperature minimum: 25 degrees</h3>
<h3>Temperature maximum: 40 degrees</h3>
<h3>Humidity: 81% </h3>
</div>
))
)
}
}
I hope it works.
I wrote some code to exercise in React. I would like somebody to explain me why if the target of the clickChange is clicked (h3), state does not update.
Below there is my main App component:
import React, { Component } from "react";
import Prova from "./components/prova";
import "./App.css";
class App extends Component {
state = {
name: "giovanni"
};
clickChange = () => {
this.setState({ name: "joe" });
};
render() {
return (
<div>
<h3>SONO APP</h3>
<Prova onClick={this.clickChange} provaProp={this.state.name} />
</div>
);
}
}
export default App;
Below another Component, imported and called (and rendered) into the main App component.
Now, as you can see i set up a method, clickChange, that, once you click on the element, it SHOULD change the state, switching "giovanni" to "joe".
The question is: why it does not trigger? I know that the rendered part of the code it's in the other component, prova, but the state it's in my App component. Therefore, the state is changed internally, without any reference to the external.
import React, { Component } from "react";
class Prova extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<p>{this.props.provaProp}</p>
</div>
);
}
}
export default Prova;
I think you just forgot to trigger the onClick event inside your Prova component.
import React, { Component } from 'react';
class Prova extends Component {
constructor(props) {
super(props)
}
render() {
return (
<div onClick={this.props.onClick}>
<p>{this.props.provaProp}</p>
</div>
)
}
}
export default Prova;
demo
class App extends React.Component {
state = {
name: "giovanni"
};
clickChange = () => {
this.setState({ name: "joe" });
};
render() {
return (
<div>
<h3>SONO APP</h3>
<Prova onClick={this.clickChange} provaProp={this.state.name} />
</div>
);
}
}
class Prova extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div onClick={this.props.onClick}>
<p>{this.props.provaProp}</p>
</div>
);
}
}
ReactDOM.render(<App/>, document.getElementById('app'));
<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="app"></div>
For props you will get it in componentWillReceiveProps()
Usage in child component
componentWillReceiveProps(props){
console.log(props.provaProps);
}
So whenever a state of parent component gets updated it updates the props as well but to get updated props in child components we use componentWillReceiveProps().
See more here
Additionally you forgot to attach click event in props
<div onClick={this.props.onClick}>
<p>{this.props.provaProp}</p>
</div>
Because you have not tiggered onClick event in child Component.
change code in Prova component as
<div onClick={this.props.onClick}>
<p>{this.props.provaProp}</p>
</div>
resolved react example here
I`m creating a react dynamic dialog where you can add functionality to the Dialog.
One way of doing this was with Composition, but I did not manage to do this via composition.
I`m not very experienced on React, so this was my first approach
I got my Modal component, and the modal has
export default class KneatModal extends React.Component {
constructor() {
super();
this.state = {
open: false
}
this.components = [];
I would add components like this
import CommentField from '../../../Modal/ModalContents/CommentField.jsx'
export default class DoApprove extends React.Component {
constructor() {
super();
}
componentDidMount() {
this._buildDialog();
}
_buildDialog() {
console.log("Building the Dialog");
this.modal.components.push(CommentField);
}
In that modal renderer, i have
<ModalContent components={ this.components } />
Then i the final renderer in ModalContent i try to render all attached components
render() {
var list = this.props.components.map((Component, key) => <Component/> );
return (
<div className='modal-contents'>
{list}
</div>
)
}
But the render method does not seems to work, i`ve tryed callin Component.render() instead of the component tag, but still could not make the sub-components render. =(
Would apreciate any help. Thanks
To be even more specific, this resumes on what im attempting
import PropTypes from 'prop-types';
import React from 'react';
import MyComponent1 from './MyComponent1.jsx'
import MyComponent2 from './MyComponent2.jsx'
export default class KneatModalContent extends React.Component {
constructor() {
super();
this.components = [MyComponent1, MyComponent2];
}
render() {
return (
<div className='modal-contents'>
{this.components.map(function (component, i) {
return <{ component } key= { i } />
})}
</div>
)
}
}
The code in main App component is as follows :
class App extends Component {
componentDidMount(){
console.log(this.ref);
debugger;
}
render() {
return (
<div>
<Header/>
{this.props.children}
<Footer/>
</div>
);
}
}
And one of the components which renders with {this.props.children} is HomePage, where are sections with refs.
The code of a HomePage is as follows :
render(){
return (
<div className="homeMain">
<section ref="info"> <Info/> </section>
<section ref="contact"> <Contact /> </section>
</div>
);
}
How can I get those refs inside App component to be able to pass them as props to header?
I'm trying to do it inside componentDidMount in App component, but console.log(this.refs) is empty.
Any advice?
EDIT
The whole App component :
import React, {Component} from 'react';
import Footer from './common/footer';
import Header from './common/header';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import * as actions from './components/homepage/login/authActions';
class App extends Component {
componentDidMount(){
console.log(this.props.children.refs);
debugger;
}
render() {
return (
<div>
<Header route={this.props.location.pathname}
language={this.props.language.labels}
authenticated={this.props.authenticated}
signoutAction={this.props.actions}
offsets={this.props.offsets}
/>
{React.cloneElement(this.props.children, {
currentLanguage: this.props.language.labels,
authenticated: this.props.authenticated
})}
<div className="clearfix"/>
<Footer currentLanguage={this.props.language.labels}/>
</div>
);
}
}
function mapStateToProps(state, ownProps) {
return {
language: state.language,
authenticated: state.auth.authenticated,
offsets: state.offsets
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(actions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
The React's main idea is passing props downward from parent to children (even to deeper levels of children - grandchildren I mean)
Therefore, when we want the parent to do something which is triggered from (or belongs to) the children, we can create a callback function in the parent, then pass it down to children as props
For your preference, this is a demonstration on how to pass callback functions downward through many levels of children and how to trigger them:
Force React container to refresh data
Re-initializing class on redirect
In your case, you can access refs from children components as follows: (using string for ref - as you stated)
Parent Component:
import React, { Component } from 'react';
// import Child component here
export default class Parent extends Component {
constructor(props){
super(props);
// ...
this.getRefsFromChild = this.getRefsFromChild.bind(this);
}
getRefsFromChild(childRefs) {
// you can get your requested value here, you can either use state/props/ or whatever you like based on your need case by case
this.setState({
myRequestedRefs: childRefs
});
console.log(this.state.myRequestedRefs); // this should have *info*, *contact* as keys
}
render() {
return (
<Child passRefUpward={this.getRefsFromChild} />
)
}
}
Child Component:
import React, { Component } from 'react';
export default class Child extends Component {
constructor(props){
super(props);
// ...
}
componentDidMount() {
// pass the requested ref here
this.props.passRefUpward(this.refs);
}
render() {
return (
<div className="homeMain">
<section ref="info"> <Info/> </section>
<section ref="contact"> <Contact /> </section>
</div>
)
}
}
ref is property of each this.props.children hence you can access ref of child component in parent via ref property on this.props.children
Make sure you access ref after componentDidMount
Edit :
Try below set of code if this works :
var myChild= React.Children.only(this.props.children);
var clone = React.cloneElement(myChild, { ref: "myRef" });