why changing state does not trigger if it's related to props - javascript

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

Related

Having some problem on using map() method on my react application

In my app i have an initial state in a component App.js it's an array of objects
Here is App.js code:
import React, { Component } from 'react';
import './App.css';
import { render } from '#testing-library/react';
// Import Used Components
import SearchBar from '../SearchBar/SearchBar';
import Playlist from '../PlayList/PlayList';
import SearchResults from '../SearchResults/SearchResults';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
searchResults: [{name: 'name1',artist: 'artist1',album: 'album1',id: 1},
{name: 'name2',artist: 'artist2',album: 'album2',id: 2}]
};
}
// Adding JSX to App Component
render() {
return (
<div>
<h1>Ja<span className="highlight">mmm</span>ing</h1>
<div className="App">
<SearchBar />
<div className="App-playlist">
<SearchResults searchResults={this.state.searchResults} />
<Playlist />
</div>
</div>
</div>
);
}
}
export default App;
I passed this initial state as a prop called searchResults to another component named .
Here is searchResults.js code :
import './SearchResults.css';
import TrackList from '../TrackList/TrackList';
class SearchResults extends React.Component {
render() {
return (
<div className="SearchResults">
<h2>Results</h2>
<TrackList tracks={this.props.searchResults}/>
</div>
);
}
}
export default SearchResults;
then I used passed this prop to another component called TrackList
here is TrackList.js code:
import React from 'react';
import './TrackList.css';
import Track from '../Track/Track';
class TrackList extends React.Component {
render() {
return(
<div className="TrackList">
{
this.props.tracks.map(track => {
return <Track track={track} key={track.id} />;
} )
}
</div>
);
}
}
export default TrackList;
In Track.js I want to map through this initial state array to render a component called Track
here is the Track.js code:
import React from 'react';
import './Track.css';
class Track extends React.Component {
renderAction() {
if (this.props.isRemoval){
return <botton className='Track-action'>-</botton>;
} else {
return <botton className='Track-action'>+</botton>;
}
};
render() {
return (
<div className="Track">
<div className="Track-information">
<h3>{this.props.track.name}</h3>
<p>{this.props.track.artist} | {this.props.track.album}</p>
</div>
<button className="Track-action">{this.renderAction}</button>
</div>
);
}
}
export default Track;
But something is wrong !! I keep getting this error:
TypeError: Cannot read property 'map' of undefined
Here is searchBar.js component code:
import React from 'react';
import './SearchBar.css';
class SearchBar extends React.Component {
render() {
return (
<div className="SearchBar">
<input placeholder="Enter A Song, Album, or Artist" />
<button className="SearchButton">SEARCH</button>
</div>
);
}
}
export default SearchBar;
HERE LINK TO THE PROJECT WITH THE SAME ERROR ON SANDBOX
https://codesandbox.io/s/upbeat-dawn-lwbxb?fontsize=14&hidenavigation=1&theme=dark
Change your TrackList component to this:
class TrackList extends React.Component {
render() {
return (
<div className="TrackList">
{this.props.tracks && this.props.tracks.map(track => {
return <Track key={track.id} track={track}/>
})}
</div>
);
}
}
You can't map through this.props.tracks if it is undefined.
The && (AND operator) is a concise way to conditionally render in React. You can think of it like a simple if statement: If the expression on the left is true, then do x.
I'll also expand on why the this.props.tracks was undefined in a certain instance in your case.
The reason that this problem is happening is your Playlist component. If you uncomment this component from your App you will notice your original code will work.
This is because your PlayList component, like your SearchResults component, also renders your TrackList component. The problem is you haven't passed your state and props down to TrackList like you did with your SearchResults component.
So an alternative solution would be to pass your state and props down from PlayList to TrackList:
App.js
// ...
<SearchResults searchResults={this.state.searchResults} />
<Playlist searchResults={this.state.searchResults}/>
// ...
PlayList.js
// ...
<TrackList tracks={this.props.searchResults}/>
// ...

ReactJS - State change is not observed in divs wrapped in sub component

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

How can get intl.formatMessage from parent component?

How can I get intl.formatMessage from parent component? I wrapped parent component with injectIntl and want send intl.formatMessage to child component. Can someone help me with that? Thank you!
Parent component
import Car from "./test3";
import { injectIntl } from "react-intl";
class Intl extends React.Component {
render() {
return (
<div>
<h1>Who lives in my garage?</h1>
<Car brand="Ford" />
</div>
);
}
}
export default injectIntl(Intl);
Child component
import { FormattedMessage} from "react-intl";
class Car extends React.Component {
yearsTranslation = () =>
this.props.intl.formatMessage({ id: "search.filter.maturity.years" });
render() {
return <h2>Hello {this.yearsTranslation()}!</h2>;
}
}
export default Car;
Just pass the prop down, like so :
class Intl extends React.Component {
render() {
return (
<div>
<h1>Who lives in my garage?</h1>
<Car brand="Ford" intl={this.props.intl}/>
</div>
);
}
}
export default injectIntl(Intl);
I thinks intl is available in the context. Please check the documentation.

Update state for component by event handle in other component in different file?

I have two component in my project one is Tag and the other is LandingTicker so i want when i click Tag componet update state for LandTicker componet, and landticker componet in different file.
how i can do that?
thank you.
Tag component code::
tag/index.js
import React from 'react';
import './index.scss';
class Tag extends React.Component {
handleClick(e) {
let tags = document.querySelectorAll('.show-clickable');
Array.from(tags).map(el => el.classList.remove('selected-tag'))
e.target.classList.add('selected-tag');
/*
Here i should update the state for LandingTicker component.
and remember any component in different file.
How i can do that???
*/
}
render() {
return (
<div
className="show-clickable"
onClick={this.handleClick}
>
click here
</div>
);
}
}
export default Tag;
LandingTicker component code::
LandingTicker/index.js
import React from 'react';
import TickerRow from './TickerRow';
import './index.scss';
class LandingTicker extends React.Component {
state = {
coin: 'USD'
}
render() {
return (
<div className="landing-ticker__body">
{selectCoin(this.state.coin)}
</div>
</div>
);
}
}
const selectCoin = (coin) => {
const coins = {
USD: ['BTCUSD', 'ETHUSD', 'EOSUSD', 'LTCUSD'],
EUR: ['BTCEUR', 'ETHEUR', 'EOSEUR'],
GBP: ['BTCGBP', 'EOSGBP'],
JPY: ['BTCJPY', 'ETHJPY'],
};
return (
coins[coin].map(el =>
<TickerRow symbol={el} key={el.toString()} />
)
);
}
export default LandingTicker;
Edit:
my component Hierarchy::
StatusTable
TagsTable
Tag
TickerSearch
LandingTickers
TickersRow
StatusTable component code::
import React from 'react';
import TagsTable from './TagsTable';
import TickerSearch from './TickerSearch';
import LandingTicker from './LandingTicker';
import './StatusTable.scss';
class StatusTable extends React.Component {
render() {
return (
<div className="status-table">
<TagsTable />
<TickerSearch />
<LandingTicker />
</div>
);
}
}
export default StatusTable;
React handle all its component data in the form of state and props(immutable). So it is easy to pass data from parent to child or one component to another using props :
Your Tag.js file:
import React, { Component } from "react";
import LandingTicker from "./LandTicker";
class Tag extends Component {
constructor(props) {
super(props);
this.state = {
trigger: true
};
}
handleClick(e) {
// do all logic here and set state here
this.setState({ trigger: this.state.trigger });
}
render() {
//And then pass this state here as a props
return (
<div className="show-clickable" onClick={this.handleClick}>
click here
<LandingTicker trigger={this.state.trigger} />
</div>
);
}
}
export default Tag;
Inside LandTicker.js file:
import React from 'react';
import TickerRow from './TickerRow';
import './index.scss';
class LandingTicker extends React.Component {
state = {
coin: 'USD'
}
render() {
//Catch your props from parent here
//i.e this.props(it contains all data you sent from parent)
return (
<div className="landing-ticker__body">
{selectCoin(this.state.coin)}
</div>
);
}
}
const selectCoin = (coin) => {
const coins = {
USD: ['BTCUSD', 'ETHUSD', 'EOSUSD', 'LTCUSD'],
EUR: ['BTCEUR', 'ETHEUR', 'EOSEUR'],
GBP: ['BTCGBP', 'EOSGBP'],
JPY: ['BTCJPY', 'ETHJPY'],
};
return (
coins[coin].map(el =>
<TickerRow symbol={el} key={el.toString()} />
)
);
}
export default LandingTicker;
I think this is the best answer for your question if you don't use state management system such as Redux or Mobx.
https://medium.com/#ruthmpardee/passing-data-between-react-components-103ad82ebd17
(you need to check third option)

How to get refs from another component in React JS

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" });

Categories