Using STATE as html ATTR in React JSX - reactjs

I am working in ReactJS, and have a URL of a photo stored in State. I want to use that photoURL to call up the image in my component render. But it when I use the normal src={this.state.photoURL} it throws an error.
Ideally, I would be able to use it as a background image in a container like this:
<div className='photo-cont' style='background-image:url({this.state.photoURL}); background-size:cover;'
I've tried this and as a normal img, with and without quotes around the curly braces, with and without the curly braces themselves.
class Foo extends Component {
state: {
photoURL: 'www.foobar.com/foo.jpg',
}
render () {
return(
<img src={this.state.photoURL}></img>
)
}
}
I keep getting the following error: 'TypeError: Cannot read property 'photoURL' of null'

It seems like you have a typo in your code. To initialize a class field (like state you should use =.
I added both an example of the photoURL as an image and as a background image of a div. Remember to set width/height/padding on the div to show the image:
class Foo extends React.Component {
state = {
photoURL: 'https://unsplash.it/400/200',
}
render () {
return(
<div>
<img src={this.state.photoURL}/>
<div style={{
width: '400px',
height: '200px',
backgroundImage: `url(${this.state.photoURL})`,
}}/>
</div>
)
}
}
ReactDOM.render(<Foo/>, document.getElementById('root'));
<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>
<div id="root"></div>

class Foo extends Component {
contructor(props) {
super(props)
this.state = {
photoURL: 'www.foobar.com/foo.jpg',
}
}
render () {
return(
<img src={this.state.photoURL}></img>
)
}
}
try to use contructor() to defined your state

import logo from './logo.png';
class Foo extends Component {
render () {
return(
<img src={logo}></img>
)
}
}
Check out their documentation:
https://facebook.github.io/create-react-app/docs/adding-images-fonts-and-files

Related

Dynamic Image not rendering

I want to dynamically render images, but nothing is show up. Here is the starter code that I am using from Import image dynamically in React component.
import React, { Component, Fragment } from 'react';
class Test extends Component{
constructor(props){
super(props);
this.state = {
image: "",
}
this.loadImage = this.loadImage.bind(this);
}
componentDidMount(){
this.loadImage("Test")
}
loadImage = imageName => {
import(`../assets/${imageName}.png`).then(image => {
this.setState({
image
});
});
};
render() {
const { image } = this.state;
return (
<Fragment>
hello
{image && <img src={image} alt="" />}
</Fragment>
);
}
}
export default Test;
Hello renders, but the image is no where to be seen. Any thoughts
You can add the string directly into your image state, without rendering it asynchronous. I don't think the import statement is needed. Once you have the string you could use a similar logic you have already in place with the image but instead if you are using webpack this might work:
<div
style={{
backgroundImage: `url(${image})`,
height: "106px"
}}
If you are not using webpack, than you can add the image state in the src attribute.

Display element based on event fired and props passed in

I am trying, to manipulate another element, by, passing props directly to it, and then have it display itself. If I pass true/false.
Live running code:
https://codesandbox.io/s/keen-dan-rt0kj
I don't know if it's possible to have a system of objects, and based on an event, tell a parent to display a child.
App.js
import React from "react";
import "./styles.css";
import Content from "./components/Content";
export default class App extends React.Component {
state = {
display: false
};
render() {
return (
<div className="App">
<button onClick={() => this.setState({ display: !this.state.display })}>
Display div
</button>
<Content display={this.state.display} />
</div>
);
}
}
./components/Content.js:
import React from "react";
export default class Content extends React.Component {
constructor(props) {
super();
this.state = {
display: props.display
};
}
render() {
const { display } = this.state;
return (
<div
id="mydiv"
className="mydiv"
style={{ display: display ? "block" : "none" }}
>
<h3>A simple div</h3>
</div>
);
}
}
Goal:
I want to based on a state, and based on fired event, display an element that already in store of root.
EDIT: I am aware that, this exists and can be used: import PropTypes from 'prop-types', however, I am not sure this is good practice, since it requires some parent or some other component to implement the props.
JUST Tried:
App:
<Content display={this.state.display} content={"Hello World"} />
Content:
<h3>{this.state.content}</h3>
It seems the passed in text, stored in Content state = {content: props.content} does get displayed, wheres, the boolean value does not work directly. Is there something wrong with sending in a bool ?
try this in your Content Component
export default class Content extends React.Component {
constructor(props) {
super();
this.state = {
};
}
render() {
return (
<>
{this.props.display?(
<div
id="mydiv"
className="mydiv"
>
<h3>A simple div</h3>
</div>
):null}
</>
);
}
}
The reason this may not be working is because you are initiating the state in a way that does not connect the display props after the component is initialized. This means that after the Content component is "constructed", the state of the Content and it's parent are not linked. This is because the constructor() function is only run once to initialize the state.
The best option you have is to not use the internal state of the Content component. Rather than initializing state with the display prop, just use the display prop in your render function.
Trying something like this might work
import React from "react";
export default class Content extends React.Component {
constructor(props) {
super(props);
}
render() {
const { display } = this.props;
return (
<div
id="mydiv"
className="mydiv"
style={{ display: display ? "block" : "none" }}
>
<h3>A simple div</h3>
</div>
);
}
}
Also I would reccommend using state in the root:
import React from "react";
import "./styles.css";
import Content from "./components/Content";
export default class App extends React.Component {
constructor(props) {
super();
state = {
display: false
};
}
render() {
return (
<div className="App">
<button onClick={() => this.setState({ display: !this.state.display })}>
Display div
</button>
<Content display={this.state.display} />
</div>
);
}
}

Show a React Skeleton Loader until a image load completely

I Have a react-loading-skeleton in my component, i have a static image in my page that i loaded using img tag, i want to show a skeleton component until the image loads.
I tried react.lazy and suspense but it only shows until the component load not works until image loads completely.
import React from 'react';
export default class SlideItems extends React.Component {
render() {
return (
<div>
<img src={require("./someImg.jpg")}/>
</div>
);
}
}
Start by adding constructor and state to class:
constructor(props) {
super(props);
this.state = {
image: '',
}
}
You can then add onLoad property to image tag like this:
<img src={require("./someImg.jpg")} onLoad={this.handleImageLoaded.bind(this)}/>
And create function that handles image loading:
handleImageLoaded() {
this.setState({ image: 'loaded' });
}
Then in render you can do something like this:
render() {
return (
<div>
{!this.state.image &&
<SkeletonComponent/>
}
<img src={require("./someImg.jpg")} onLoad={this.handleImageLoaded.bind(this)}/>
</div>
);
}

on click i want to generate alert in react js method

This is my code:
generateAlert = () => {
alert('hi');
}
return <Tile
click={(index)=>{this.generateAlert}}
title={tile.title}
value={tile.value}
key={tile.id}
/>
This is the error I'm getting:
Expected an assignment or function call and instead saw an expression no-unused-expressions
Search for the keywords to learn more about each error.
First, I do wonder if in your Component you have an array of Tile data, and you want to render a Tile for each entry of the array (I thought so because you added the key prop to Tile).
Anyways, I made an example similar to what you want to achieve, and it's working. Look at this:
const Tile = (props) => {
return (
<div className="Tile">
<h3>{props.title}</h3>
<div onClick={props.click}>
{props.value}
</div>
</div>
);
}
class App extends React.Component {
constructor(props) {
super(props);
}
generateAlert = () => {
alert("Hi");
}
render() {
return (
<Tile
click={this.generateAlert}
title={"This isa a Title"}
value={"This is the value"} />
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
#import url(https://fonts.googleapis.com/css?family=Montserrat);
body {
font-family: 'Montserrat', sans-serif;
}
<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>
<div id='root'></div>
Now, I may help you in a deeper way if you would post the code of the Component that wants to render Tile; maybe, there are some error in that.
Hei!
If it's a function invocation inside your component's onClick function, you need to add () after this.generateAlert in your component
So it's gonna be like:
return <Tile
click={(index)=>{this.generateAlert()}}
title={tile.title}
value={tile.value}
key={tile.id}
/>
Otherwise, you can use your function as a onClick callback per se.
In that case you need to have it like this:
return <Tile
onClick={this.generateAlert}
title={tile.title}
value={tile.value}
key={tile.id}
/>
Cheers!
I will do in this way:
Q: why I export Tile to new component?
A: As each component should be as short as possible. There is a many advantages to doing in this way
like: "easy to find bugs (testing)".
import React, { Component } from "react";
import Tile from "./Tile";
import "./App.css";
class App extends Component {
constructor(props) {
super(props);
this.generateAlert = this.generateAlert.bind(this);
}
generateAlert = () => {
alert("Hi");
};
render() {
return (
<Tile
click={this.generateAlert}
title={"This isa a Title"}
value={"This is the value"}
/>
);
}
}
export default App;
and file Tile.js:
import React, { Component } from "react";
export default class Tile extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<button onClick={this.props.click}>click me</button>
<p>{this.props.title}</p>
<p>{this.props.value}</p>
</div>
);
}
}
This file Tile.js are ready for future addons but if you want to use only like it is now I would recommend to change into stateless component:
import React from "react";
const Tile = props => {
return (
<div>
<button onClick={props.click}>click me</button>
<p>{props.title}</p>
<p>{props.value}</p>
</div>
);
};
export default Tile;

React constructor called only once for same component rendered twice

I expected this toggle to work but somehow the constructor of component <A/> is called only once. https://codesandbox.io/s/jvr720mz75
import React, { Component } from "react";
import ReactDOM from "react-dom";
class App extends Component {
state = { toggle: false };
render() {
const { toggle } = this.state;
return (
<div>
{toggle ? <A prop={"A"} /> : <A prop={"B"} />}
<button onClick={() => this.setState({ toggle: !toggle })}>
toggle
</button>
</div>
);
}
}
class A extends Component {
constructor(props) {
super(props);
console.log("INIT");
this.state = { content: props.prop };
}
render() {
const { content } = this.state;
return <div>{content}</div>;
}
}
ReactDOM.render(<App />, document.getElementById("root"));
I already found a workaround https://codesandbox.io/s/0qmnjow1jw.
<div style={{ display: toggle ? "none" : "block" }}>
<A prop={"A"} />
</div>
<div style={{ display: toggle ? "block" : "none" }}>
<A prop={"B"} />
</div>
I want to understand why the above code is not working
In react if you want to render same component multiple times and treat them as different then you need to provide them a unique key. Try the below code.
{toggle ? <A key="A" prop={"A"} /> : <A key="B" prop={"B"} />}
Since that ternary statement renders results in an <A> component in either case, when the <App>'s state updates and changes toggle, React sees that there is still an <A> in the same place as before, but with a different prop prop. When React re-renders it does so by making as few changes as possible. So since this is the same class of element in the same place, React doesn't need to create a new element when toggle changes, only update the props of that <A> element.
Essentially, the line
{toggle ? <A prop="A"/> : <A prop="B"/> }
is equivalent to
<A prop={ toggle ? "A" : "B" }/>
which perhaps more clearly does not need to create a new <A> component, only update the existing one.
The problem then becomes that you set the state.content of the <A> using props.prop in the constructor, so the state.content is never updated. The cleanest way to fix this would be to use props.prop in the render method of the <A> component instead of state.content. So your A class would look like this:
class A extends Component {
render() {
const { prop } = this.props;
return <div>{ prop }</div>;
}
}
If you must take the prop prop and use it in the <A> component's state, you can use componentDidUpdate. Here's an example:
class A extends Component {
constructor(props) {
super(props);
this.state = {content: props.prop};
}
componentDidUpdate(prevProps) {
if (prevProps.prop !== this.props.prop) {
this.setState({content: this.props.prop});
}
}
render() {
const { content } = this.state;
return <div>{ content }</div>
}
}
React will only call the constructor once. That's the expected outcome.
Looks like you're trying to update the state of the component A based on the props.
You could either use the prop directly or use the componentDidUpdate lifecycle method, as Henry suggested. Another way is using the static method getDerivedStateFromProps to update the state based on the prop passed.
static getDerivedStateFromProps(props, state) {
return ({
content: props.prop
});
}

Resources