I have two components, let's say componentA and componentB. Both component import a .gif file, let's say image.gif. image.gif does not loop, so it should be played once only if it's not updated.
Initially componentA renders image.gif but not componentB. So the image.gif inside componentA is played once. When I render image.gif inside componentB, image.gif inside componentA is played again, which is not wanted.
Can it be done? Thanks!
Edit: Some simple codes for reproducing:
Component A
import image as './image.gif'
export default class componentA extends React.PureComponent {
render() {
return (
<div>
<img src={image} />
</div>
);
}
}
Component B
import image as './image.gif'
export default class componentA extends React.PureComponent {
render() {
return (
<div>
{this.props.show ? <img src={image} /> : null}
</div>
);
}
}
App
import componentA from './componentA.react'
import componentB from './componentB.react'
export default class App extends React.Component {
componentWillMount() {
this.setState({
show: false,
});
}
componentDidMount() {
// ...or some moment when the App thinks componentB should update
this.setTimeout(() => {
this.setState({
show: true,
});
}, 4000);
}
render() {
return (
<componentA />
<componentB show={this.state.show} />
);
}
}
I'd suggest to use a bit more complex (and more flexible) structure:
use static and animated gifs and swap them with javascript.
Like was suggested in similar cases:
Stop a gif animation onload, on mouseover start the activation
JavaScript to control image animation?
As this.setState runs the this.render method, your 2 components are rerendered.
You can try the following :
Component A :
export default class componentA extends React.Component {
render() {
return (
<div>
<img src={image} />
</div>
);
}
}
Component B :
export default class componentB extends React.Component {
constructor () {
super(props)
this.state = { show: false }
}
componentDidMount() {
this.setTimeout(() => {
this.setState({
show: true,
});
}, 4000);
}
render() {
return (
<div>
{this.state.show ? <img src={image} /> : null}
</div>
);
}
}
App :
import componentA from './componentA.react'
import componentB from './componentB.react'
export default class App extends React.Component {
render() {
// Carefull here, you have to return a single `node` in the render method
return (
<div>
<componentA />
<componentB />
</div>
);
}
}
Related
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}/>
// ...
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.
Good afternoon friends,
My pages and components are arranged in the main class of my application, can I pass some results from any component or page to the main class and get this property from main class to any other component.
To describe question well I will show an example:
This is my main class App.js
import React, {Component} from 'react';
import { BrowserRouter as Router, Route} from "react-router-dom";
import HomePage from "./Pages/HomePage";
import NavBar from "./Components/NavBar";
import PaymentStatus from "./Pages/PaymentStatus";
class App extends Component {
constructor(props) {
super(props);
this._isMounted = true;
this.state = {};
};
render() {
return (
<Router>
<NavBar/>
<Route name={'Home'} exact path={'/'} component={HomePage}/>
<Route name={'PaymentStatus'} exact path={'/payment-status/:tId'} component={PaymentStatus}/>
</Router>
);
}
}
export default App;
Now my navigation bar component: NavBar.js
import React, {Component} from 'react';
class NavBar extends Component {
constructor(props) {
super(props);
this._isMounted = true;
this.state = {};
};
_makeSomething =async() => {
// Somw function that returns something
}
render() {
return (
<div>
<div id={"myNavbar"}>
<div>
<a onClick={()=>{this._makeSomething()}} href={'/'}/> Home</a>
<a onClick={()=>{this._makeSomething()}} href={"/payment-status"} />Payment Status</a>
</div>
</div>
);
}
}
export default NavBar;
HomePage.js
import React, {Component} from 'react';
class HomePage extends Component {
constructor(props) {
super(props);
this._isMounted = true;
this.state = {};
};
async componentDidMount() {
console.log(this.props.match.params.tId)
};
render() {
return (
<div>
<div id={"main"}>
<div>
<p>This is home page</p>
</div>
</div>
);
}
}
export default HomePage;
PaymentStatusPage.js
import React, {Component} from 'react';
class PaymentStatusPage extends Component {
constructor(props) {
super(props);
this._isMounted = true;
this.state = {};
};
async componentDidMount() {
console.log(this.props.match.params.tId)
};
render() {
return (
<div>
<div id={"status"}>
<div>
<p>This is payment Status Page</p>
</div>
</div>
);
}
}
export default PaymentStatusPage;
Now here is the question:
Can I pass to App.js events (or props) when HomePage.js or PaymentStatusPage.js or when something was changed in NavBar.js
Also, want pass received peprops to any component.
Thank you.
You can decalare method in class App and then pass it to another component via props.
For example
Then you can call this method in MyComponent and pass some value to it. This is the way you pass value from subcomponent to parent component. In method in App you can simply use setState.
What's left to do is to pass this new state attribute to another component via props.
To pass value to component, while using you have to change
<Route component={SomeComponent}
To
<Route render={() => <SomeComponent somethingChanged={this.somethingChangedMethodInAppClass}}/>
Hope it helps!
EDIT: You can also use Redux to externalize state and reuse it in child components
You have two options here:
Keep all of your state in your parent component, App, and pass any props down to your children component, even actions that could update the parent state. If another children uses that state, then that child will be rerendered too.
Manage your state with Redux and make it available for all your components.
I created a small example out of your scenario.
In this example, the App component has a state with a property called title and a function that is passed down via props to the Navbar.
class App extends Component {
constructor(props) {
super(props);
this._isMounted = true;
this.state = {
title: "Home Page"
};
}
_makeSomething = title => {
this.setState({ title: title });
};
render() {
return (
<Router>
<NavBar clicked={this._makeSomething} />
<Route
name={"Home"}
exact
path={"/"}
component={() => <HomePage title={this.state.title} />}
/>
<Route
name={"PaymentStatus"}
exact
path={"/payment-status/:tId"}
component={() => <PaymentStatus title={this.state.title} />}
/>
</Router>
);
}
}
The components HomePage and PaymentStatus will get that title as props from the App's state and NavBar will get the _makeSomething function as props. So far, all that function does is update the state's title.
class NavBar extends Component {
constructor(props) {
super(props);
this._isMounted = true;
this.state = {};
}
render() {
return (
<div>
<div id={"myNavbar"}>
<NavLink
onClick={() => {
this.props.clicked("Home Page");
}}
to={"/"}
>
{" "}
Home
</NavLink>
<NavLink
onClick={() => {
this.props.clicked("Payment Page");
}}
to={"/payment-status/1"}
>
Payment Status
</NavLink>
</div>
</div>
);
}
}
In the Navbar, when the function I passed down from App as props is clicked, it will go all the way back to the App component again and run _makeSomething, which will change the App's title.
In the mantime, the components HomePage and PaymentStatus received title as props, so when the state's title is changed, these two children component will change too, since their render function relies on this.props.title.
For example, HomePage:
class HomePage extends Component {
constructor(props) {
super(props);
this._isMounted = true;
this.state = {};
}
async componentDidMount() {
console.log(this.props.match.params.tId);
}
render() {
return (
<div>
<div id={"main"}>
<p>This is {this.props.title}</p>
</div>
</div>
);
}
}
Like I said before, by keeping your state in the parent component and sending down to the children component just what they need, you should be able to accomplish what you need.
A note: I did change the anchor tag from <a> to NavLink which is what you're supposed to use with react-router-dom if you don't want a complete refresh of the page.
The full code can be found here:
Have a look at Context. With this you can pass an object from a Provider to a Consumerand even override properties with nested providers: https://reactjs.org/docs/context.html
AppContext.js
export const AppContext = React.createContext({})
App.js
someFunction = ()=>{
//implement it
}
render() {
const appContext = {
someFunction: this.someFunction
}
return (
<AppContext.Provider value={appContext}>
<Router>
<NavBar/>
<Route name={'Home'} exact path={'/'} component={HomePage}/>
<Route name={'PaymentStatus'} exact path={'/payment-status/:tId'} component={PaymentStatus}/>
</Router>
</AppContext>
);
}
Homepage.js
class HomePage extends Component {
constructor(props) {
super(props);
this._isMounted = true;
this.state = {};
};
async componentDidMount() {
console.log(this.props.match.params.tId)
this.props.appContext.someFunction(); //calls the function of the App-component
};
render() {
return (
<div>
<div id={"main"}>
<div>
<p>This is home page</p>
</div>
</div>
);
}
}
export default (props) => (
<AppContext.Consumer>
{(appContext)=>(
<HomePage {...props} appContext={appContext}/>
)}
</AppContext.Consumer>
)
You can also use this mechanic with function components. I'm normally encapsulating the Consumer to an extra component. So all values available for the component as normal property and not just inside the rendered components.
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 have no idea how to make my app work.
I have a component ContactAdd that onClick must render component ModalWindow. ModalWindow has a parameter isOpened={this.state.open}. How to control this state from parent component?
import React from 'react';
import ContactAddModal from './ContactAddModal.jsx';
import './ContactAdd.css';
export default class ContactAdd extends React.Component {
constructor(props){
super(props);
}
render() {
return (
<div className="add-contact" onClick={ ??????? }>
<img src="./img/add.png" />
<ContactAddModal/>
</div>
)
}
}
import React from 'react';
export default class ContactAddModal extends React.Component {
constructor(props){
super(props);
this.state = {
show: false,
};
this.handleCloseModal = this.handleCloseModal.bind(this);
}
handleCloseModal () {
this.setState({ show: false });
}
render() {
return (
<div className="modal" isOpened={this.state.show}>
<button onClick={this.handleCloseModal}>Close Modal</button>
</div>
)
}
Making the modal a stateless component might be simpler here. The tradeoff being that you would have to handle the closing for every modal, which I think is acceptable. This answer is just an option and by no means an absolute truth.
It could look something like that
export default class ContactAdd extends React.Component {
constructor(props){
super(props);
this.state = {
showModal: true
};
this.hideModal = this.hideModal.bind(this);
}
hideModal() {
this.setState({
showModal: false
});
}
render() {
return (
<div className="add-contact">
<img src="./img/add.png" />
<ContactAddModal handleClose={this.hideModal} isOpened={this.state.showModal} />
</div>
)
}
}
Now the modal:
export default class ContactAddModal extends React.Component {
render() {
return (
<div className="modal" isOpened={this.props.isOpened}>
<button onClick={this.props.handleClose}>Close Modal</button>
</div>
)
}