Setting background image as prop in react - reactjs

I have a react component that needs to take in a background image from the parent Play component, not set in the stylesheet. I'm trying to pass it as a prop to the component, but not sure quite sure how to get this working:
Main component
import React from 'react'
import ReactDOM from 'react-dom'
import DeviceBlock from '/DeviceBlock/DeviceBlock'
const Play = {
init() {
const container = document.getElementById('blocker-container');
if(container) {
ReactDOM.render(
<DeviceBlock
backgroundImage={'background-image-url.png'}
logo={'url-to-logo.png'} />,
container
)
}
}
}
document.addEventListener('DOMContentLoaded', () => {
Play.init()
})
Blocking component imported into above Play component
import React from 'react'
import './DeviceBlock.scss'
function DeviceBlock (props) {
constructor(props) {
super(props)
this.state = {
backgroundImage: backgroundImage,
logo: logo;
}
}
return (
<div className='device-blocking' style={this.state.backgroundImage}>
<div className='container'>
<div className='logo'>
<img src='{{this.state.logo}}' />
</div>
<div className='message'>
<h1>Content</h1>
<a href='/' className='btn btn--primary btn--lg'>Link</a>
</div>
</div>
</div>
)
}
export default DeviceBlock

You should import your background img
import BackgroundImage from '...png'; // Relative Path to BackgroundImage
import Logo from '...png'; // Relative Path to Logo
and in your state
this.state = {
backgroundImage: `url(${BackgroundImage})`,
logo: Logo
};
Also I noticed that your <img> is wrong
<img src='{{this.state.logo}}' />
Should be
<img src={this.state.logo} />
Also note I saw that you added a constructor inside a function.
If you want to create a react component with state you need to make the component a class.
class DeviceBlock extends React.Component {
constructor(props) {
super(props);
this.state = {}
}
render() {
...
}
}
EDIT: This is the updated code for your edit based on passing through props.
import React from 'react'
import ReactDOM from 'react-dom'
import DeviceBlock from '/DeviceBlock/DeviceBlock'
import BackgroundImage from '../url-to-background.png'; // Path to BackgroundImage
import Logo from 'url-to-logo.png'; // Relative Path to Logo
const Play = {
init() {
const container = document.getElementById('blocker-container');
if (container) {
ReactDOM.render(
<DeviceBlock
backgroundImage={BackgroundImage}
logo={Logo}
/>,
container
)
}
}
}
document.addEventListener('DOMContentLoaded', () => {
Play.init()
})
import * as React from 'react'
import './DeviceBlock.scss'
function DeviceBlock (props) {
return (
<div
className='device-blocking'
style={{ backgroundImage: `url(${props.backgroundImage})` }}
>
<div className='container'>
<div className='logo'>
<img src={props.logo} />
</div>
<div className='message'>
<h1>Content</h1>
<a href='/' className='btn btn--primary btn--lg'>Link</a>
</div>
</div>
</div>
);
}
export default DeviceBlock
Note my solution only works if your images are from your own server and processed from webpack and not from another URL.
If the images are from another URL you shouldn't import the URLs and add them directly to the props
<DeviceBlock
backgroundImage="url-to-background.png",
logo="url-to-logo.png"
/>

Style is an object. Try it like this:
style={{backgroundImage: this.state.backgroundImage}}
Further, you cannot have a constructor and use state within a functional component. Either turn your component into a regular react component and keep the state, or have the state part in a regular react component and pass it down as props into the functional component. This is also mentioned in the answer by #Kenneth Truong
To turn it into a regular react component you can do the following:
class DeviceBlock extends React.Component {
constructor(props) {
super(props)
this.state = {
backgroundImage: "url('url-to-background.png')",
logo: "url('url-to-logo.png')";
}
}
render() {
return (
<div className='device-blocking' style={{backgroundImage: this.state.backgroundImage}}>
<div className='container'>
<div className='logo'>
<img src='{{this.state.logo}}' />
</div>
<div className='message'>
<h1>Content</h1>
<a href='/' className='btn btn--primary btn--lg'>Link</a>
</div>
</div>
</div>
)
}
}
You can see it working here:
https://codepen.io/anon/pen/zWexzO
UPDATE
Based on the updated question, you can also keep it as a functional component and pass down props:
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
backgroundImage: "url('url-to-background.png')",
logo: "url('url-to-logo.png')";
}
}
render() {
return (
<DeviceBlock
backgroundImage={this.state.backgroundImage}
logo={this.state.logo}/>
)
}
}
const DeviceBlock = (props) => {
return (
<div className='device-blocking' style={{backgroundImage: props.backgroundImage}}>
<div className='container'>
<div className='logo'>
<img src='{{props.logo}}' />
</div>
<div className='message'>
<h1>Content</h1>
<a href='/' className='btn btn--primary btn--lg'>Link</a>
</div>
</div>
</div>
)
}
https://codepen.io/anon/pen/OvdPOQ
The app component is simply demonstrating that you can pass down some dynamic state. You can, of course, render the DeviceBlock component directly from your Play.init function like you do in your provided code (then you can just ignore the App component part).

I was able to get the images to show in the child component by passing the images into <DeviceBlock /> in the parent like so:
<DeviceBlock
backgroundImage="background-image-url.png"
logo="logo-image-url.png" />,
container
and then taking them as props in the child
<div className='device-blocking' style={{ backgroundImage: `url(${props.backgroundImage})` }}>
<div className='container'>
<div className='logo'>
<img src={props.logo} />
</div>
<div className='message'>
<h1>Content</h1>
<a href='/' className='btn btn--primary btn--lg'>Link text</a>
</div>
</div>
</div>

Related

Using props and other attributes with react component definition

I have created a react component and I'm reusing it in other components.
<SC_Button label = "English" btnStyle = "sc-btn-default--sinhala mb-2" onClick={this.handleClick}/>
But function defined at onClick does not execute because it's with props passed to the component. I guess react reads onClick as a prop as well. I'm quite new to react.
Below way works. But I don't want to wrap my react component with an extra div due to a styling issue.
<div onClick={this.handleClick} >
<SC_Button label = "English" btnStyle = "sc-btn-default--sinhala mb-2"/>
</div>
Is there any way to use props along with other attributes in react component definitions ?
Edit :
Parent Component
import React from 'react';
import { withRouter } from "react-router-dom";
import SC_Button from '../components/button';
class Home extends React.Component {
handleClick = () => {
this.props.history.push('/page2');
}
render() {
return (
<div className="container-fluid">
<div className="row sc-overlay sc-overlay-main">
<div className="col-md-12 col-xl-5 offset-xl-1">
<span className = "sc-overlay-main__left">
<span className = "sc-main-image">
<img src={require('../assets/dialog_logo.png')} />
</span>
<h1 className="mt-4">Welcome to MyDialog</h1>
</span>
</div>
<div className="col-md-12 col-xl-5">
<div className="row sc-overlay-main__right">
<label>Choose your preferred language</label>
<SC_Button label = "සිංහල" btnStyle = "sc-btn-default--sinhala mb-2" onClick={this.handleClick}/>
<SC_Button label = "தமிழ்" btnStyle = "sc-btn-default--tamil mb-2" />
<SC_Button label = "English" btnStyle = "sc-btn-default--english mb-2" />
</div>
</div>
</div>
</div>
);
}
}
export default withRouter(Home);
SC_Button Component
import React from 'react';
import { withRouter } from "react-router-dom";
class SC_Button extends React.Component{
constructor(props) {
super(props);
}
render() {
return (
<button type="button" className={`sc-btn-default ${ this.props.btnStyle }`}>{this.props.label}</button>
);
}
}
export default withRouter(SC_Button);
Your <SC_Button /> component, or any custom component you make, doesn't automatically implement an event handler. You're essentially just giving it yet another prop, called onClick, that it just throws away. You have to use the callback you're passing it in the DOM elements it returns:
SC_Button.js
import React from 'react';
import { withRouter } from "react-router-dom";
class SC_Button extends React.Component{
constructor(props) {
super(props);
}
render() {
return (
<button
type="button"
className={`sc-btn-default ${ this.props.btnStyle }`}
onClick={this.props.handleClick}
>
{this.props.label}
</button>
);
}
}
export default withRouter(SC_Button);
There is no need to define a handleClick function in the component, since you will be passing it as a prop every time you instantiate one. This allows different instances to have different behaviors.

Why is my component not effectively receiving props?

I keep getting a message that the item I'm trying to access via props is undefined. Can you please tell me why it's not working?
Here is where the instance where the props are attempting to be passed... I'm specifically talking about the tellus and yourTrial props.
import React from 'react'
import Info from './components/Info'
import Upsell from '../../../general/components/order/components/Upsell'
import FormTwo from '../../../general/components/forms/FormTwo'
import Footer from '../../../cbd-desktop/components/layout/Footer'
export default class Order extends React.Component {
render() {
return (
<div>
<div>
<div style={styles.header}>
<img style={styles.leaf} src="./resources/desktop-img/leaves_top.png" />
<img style={styles.logo} src="./resources/desktop-img/logo_white.png" />
</div>
<div style={styles.wrapper}>
<div style={styles.leftWrapper}>
<Info />
<Upsell styles={upsellStyles} />
</div>
<FormTwo styles={formStyles} tellus="Tell us where to send" yourTrial="YOUR TRIAL BOTTLE"} />
</div>
</div>
<Footer style={styles.footer}/>
</div>
)
}
}
And here is where I am trying to display these values on the child... towards the top in two h2s
import React from 'react'
import { connect } from 'react-redux'
import { stepTwoSubmit, saveBillingData } from
'../../actions/formStepTwoActions'
import { addReceiptProduct } from '../../actions/receiptActions'
import FormTwoInputs from './components/FormTwoInputsComponent.jsx'
import Throbber from '../throbber/Throbber'
import FormWarning from './components/FormWarningComponent.jsx'
import Button from '../../../cbd-desktop/components/layout/Button'
const mapStateToProps = state => ({
state:state,
config:state.config,
downsellProduct:state.downsell.downsellProduct || {},
receiptProducts:state.receipt.receiptProducts || []
})
const mapDispatchToProps = {
stepTwoSubmit,
saveBillingData,
addReceiptProduct
}
#connect(mapStateToProps, mapDispatchToProps)
export default class FormTwo extends React.Component {
constructor(props) {
super(props)
componentWillReceiveProps(nextProps) {
let formTwoResponse = nextProps.state.stepTwo.formTwoResponse
this.checkOrderStatus(formTwoResponse)
}
componentDidMount() {
this.fillMainOrder()
this.calculateViewers()
this.calculateTimer()
}
render() {
let { state, props, inputs, onInputFocus, saveInputVal, styles } = this,
CustomTag = props.labels ? 'label' : 'span',
{ submitting, formWarning } = state,
{ invalidInputID, text, visible } = formWarning
return (
<div style={styles.formWrapper}>
<p style={styles.yellowBanner}>{this.state.viewers} people viewing this product right now</p>
<div style={styles.formInnerWrapper}>
<div style={styles.headerWrapper}>
<h2 style={styles.header}>{this.props.tellus}</h2>
<h2 style={styles.header}>{this.props.yourTrial}</h2>
</div>
<div style={styles.weAccept}>
<p style={styles.weAcceptText}>We Accept:</p>
<img style ={styles.cardImage} src="resources/desktop-img/cards.png" />
</div>
<form onSubmit={this.submit}>
<FormTwoInputs onInputFocus={onInputFocus} saveInputVal={saveInputVal} CustomTag={CustomTag} styles={styles} />
<FormWarning visible={visible} invalidInputID={invalidInputID} text={text} />
<Button style={styles.button} buttonText="RUSH MY TRIAL" />
</form>
</div>
<img src="resources/desktop-img/secure.png" />
<div style={styles.urgencyWrapper}>
<div style={styles.urgencyTextWrapper}>
<span style={styles.redText}>{this.state.viewers} people are viewing this offer right now -</span>
<span style={styles.blueText}>{this.state.counter}</span>
</div>
<p style={styles.blueText}>Claim Your Bottle Now</p>
</div>
<Throbber throbberText='Confirming your shipment...' showThrobber={submitting} />
</div>
)
}
}

Communicating between independent components react

I have been facing a problem, I would like to write a div every time a specific button is clicked, but the button is in one component and the menu in which I want the div to be created is in another component as well , and both main components are in one component, is there a way of making this happen? So the structure of it all is something like this:
This is the root component with the smaller components in it:
export class Root extends React.Component {
render() {
return(
<div className="Body">
<div id="SideBar" ><SideMenu/></div>
<MenuCocktails/>
<MenuBeers/>
<MenuVines/>
<MenuLemonades/>
<MenuSoftDrinks/>
<MenuCollection/>
<MenuCharger/>
<div id="BottomMenu"><BottomMenu/></div>
</div>
)
}
}
this is the menu that I wanted to div to be created in:
export class BottomMenu extends React.Component{
constructor() {
super();
this.state = {
shown: ''
};
}
toggleMenu = () => {
this.setState({shown: this.state.shown ? '' : 'visible'});
}
render() {
return(
<div>
<button
id="button"
className={this.state.shown}
onClick={this.toggleMenu}
>
My Cart
</button>
<div id="Header" className={this.state.shown}>
<button id="CheckOutButton">Check Out</button>
</div>
</div>
);
}
}
and this is one of the components with the button:
import React from "react";
import styles from "./MenuCocktails.css";
import plus from "./plus.png";
export class NiğdeGazozu extends React.Component{
render() {
return (
<div id="menu-items-1">
<div id="item-name">NIĞDE GAZOZU</div>
<div id ="price-item">8 TL</div>
<button id="plusbutton"><img src={plus}/></button>
</div>
);
}
}

Passing state in React from two different components

I have a TopNab bar (component), which contains a SearchBar component. My Main component is rendering out TopNav, the main container component (Profile), and the Footer component. I want my SearchBar component to pass its state to the main container component so that the Profile component can use it.
What I am trying to build:
A user types a name into search bar and submits.
The profile matching the user name is displayed in the main container component.
Right now I have a component that can render out user profiles. I also have a component thats state updates to the user submitted value. What I need to do is pass this user submitted value to my profile component in order to render out the correct profile.
Is this possible or do I need to rebuild my components so the search is included in the Profile component?
SearchBar
import React, { Component, PropTypes } from 'react';
import Profile from './Profile';
class SearchBar extends Component {
constructor(props){
super(props)
this.state = {
name: ''
}
}
handleChange(e) {
this.setState({
name: e.target.value
});
}
handleSubmit(e) {
e.preventDefault();
console.log("searching for NAME " + this.state.name);
let profileName = this.state.name;
//PASS STATE TO PROFILE COMPONENT
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit.bind(this)}>
ARMORY BETA
<input type="text" placeholder="Enter Name"
name="name"
value={this.state.name}
onChange={this.handleChange.bind(this)} />
<button className="btn btn-success" type="submit">Search</button>
</form>
</div>
)
}
}
export default SearchBar;
Profile
import React, { Component, PropTypes } from 'react';
import SearchBar from './SearchBar';
import ProfileContainer from '../containers/ProfileContainer';
class Profile extends Component {
render() {
return (
<div className="cols2">
<div>[IMG]</div>
<div>
<ProfileContainer name={this.props.name}/>
</div>
</div>
)
}
}
Profile.PropTypes = {
name: PropTypes.string
}
Profile.defaultProps = {
name: ''
}
export default Profile;
Main
import React, { Component } from 'react';
import TopNav from './TopNav';
import Footer from './Footer';
import Profile from './Profile';
class Main extends Component {
render() {
return (
<div className="container-fluid">
<div className="row">
//TopNav calls SearchBar
<TopNav />
</div>
<div className="row">
<Profile />
</div>
<div className="row">
<Footer />
</div>
</div>
)
}
}
export default Main;
In Main, you should add a prop to <TopNav /> that points to a handler method that will propagate the profileName state change back to Main. This, in turn, will cause Profile to be re-rendered. The handler method takes one argument profileName and is called from the handleSubmit method in TopNav. Here's the code:
SearchBar
class SearchBar extends Component {
. . .
handleSubmit(e) {
e.preventDefault();
console.log("searching for NAME " + this.state.name);
let profileName = this.state.name;
this.props.handleProfileChange(profileName);
}
. . .
}
SearchBar.propTypes = {
handleProfileChange: React.PropTypes.func.isRequired,
}
Main
class Main extends Component {
constructor(props) {
super(props);
this.state = { profileName: '' }
handleProfileChange = this.handleProfileChange.bind(this);
}
handleProfileChange(profileName) {
// This state change will force Profile component to be re-rendered
this.setState( { profileName });
}
render() {
return (
<div className="container-fluid">
<div className="row">
//TopNav calls SearchBar
<TopNav handleProfileChange={this.handleProfileChange} />
</div>
<div className="row">
<Profile profileName={this.state.profileName} />
</div>
<div className="row">
<Footer />
</div>
</div>
)
}
You'll need to expose a property on SearchBar that accepts a callback that will be called to indicate to its parent that the form was submitted (e.g. onSubmit)...
handleSubmit(e) {
e.preventDefault();
console.log("searching for NAME " + this.state.name);
let profileName = this.state.name;
//PASS STATE TO PROFILE COMPONENT
this.props.onSubmit(yourFormData);
}
...TopNav won't handle onSubmit itself, but just pass it on up to its own parent (perhaps renaming to "onSearchBarSubmit" along the way to make the name clearer from the perspective of TopNav's parent):
class TopNav extends Component {
render() {
return (
<div className="container-fluid">
<SearchBar onSubmit={this.props.onSearchBarSubmit}
</div>
);
}
}
class Main extends Component {
render() {
return (
<div className="container-fluid">
<div className="row">
<TopNav onSearchBarSubmit={ (criteria) => this.searchForStuff(criteria) } />
</div>
<div className="row">
<Profile data={this.state.stuffYouGotBackFromSearch} />
</div>
<div className="row">
<Footer />
</div>
</div>
)
}
}
...OR, in some cases, it can be desirable to un-nest the components, allowing SearchBar as one of TopNav's props.children. This allows you to handle onSubmit directly within Main, and pass anything it receives onto Profile:
class Main extends Component {
render() {
return (
<div className="container-fluid">
<div className="row">
//TopNav calls SearchBar
<TopNav>
<SearchBar onSubmit={ (criteria) => this.searchForStuff(criteria) } />
</TopNav>
</div>
<div className="row">
<Profile data={this.state.stuffYouGotBackFromSearch} />
</div>
<div className="row">
<Footer />
</div>
</div>
)
}
}
...a side-benefit of un-nesting is that it would allow you to use TopNav and Searchbar independently.

ReactJS changing page content through an array of content components by click event

I stored content component of 4 pages in an array. And I want after clicking next button(in footer component), page layout will render the next content component in the array, does anyone have an idea for that?
Many thanks! Here are my files
**** Content.jsx ****
import React from 'react';
import Start from './Start.jsx';
import Time from './Time.jsx';
import Information from './Information.jsx';
import End from './End.jsx';
export default class Content extends React.Component {
render(){
const Contents = [ <Start />, <Time />, <Information />, <End /> ];
return (
<div className="container">
<div className="row">
{Contents[0]}
</div>
</div>
);
}
};
**** Footer.jsx ****
import React from 'react';
import Content from './Content.jsx';
export default class Footer extends React.Component {
render(){
const button = {
margin: "10em 1em 0 1em"
};
return (
<div className="row">
<div className="col-sm-offset-5 col-sm-2 text-center">
<button className="btn btn-secondary" style={button}>Back</button>
<button className="btn btn-success" style={button}>Next</button>
</div>
</div>
)
}
}
you want to have an array position to know what to transition to.
so in your footer you want to know what the index is and you want to pass the click event through to a parent component to handle the data transfer
export default class Footer extends Component {
viewNext(e){
e.preventDefault();
this.props.setContent(this.props.currentIndex + 1);
}
viewPrevious(e){
e.preventDefault();
this.props.setContent(this.props.currentIndex - 1);
}
render(){
const button = {
margin: "10em 1em 0 1em"
};
return (
<div className="row">
<div className="col-sm-offset-5 col-sm-2 text-center">
<button className="btn btn-secondary" style={button} onClick={this.viewPrevious.bind(this)}>Back</button>
<button className="btn btn-success" style={button} onClick={this.viewNext.bind(this)}>Next</button>
</div>
</div>
)
}
}
now, when you are rendering the footer you want to pass through the relative info
class App extends Component {
constructor(props){
super(props);
this.state = {currentIndex: 0};
this.setContent = this.setContent.bind(this);
}
setContent(index){
this.setState({currentIndex: index});
}
render(){
let {currentIndex} = this.state;
return (
<div>
<Content currentIndex={currentIndex}/>
<Footer setContent={this.setContent} currentIndex={currentIndex}/>
</div>
)
}
}
finally in the content component you need to accept this index and use it as well
export default class Content extends Component {
constructor(props){
super(props);
this.componentsArr = [ <Start />, <Time />, <Information />, <End /> ]; // I moved this array to the constructor so this way you aren't creating it every single render. just need to make it once.
}
render(){
const ViewComponent = this.componentsArr[this.props.currentIndex];
return (
<div className="container">
<div className="row">
{ViewComponent}
</div>
</div>
);
}
}
So here is some homework for you. Add validation on viewing the next component or the previous. meaning what happens when you are viewing the last component (<End />) and you click the next button? Hint: You need to move the componentsArr logic to App as well and pass its length to the footer for index position purposes.
In parent component of both Content and Footer add a method setActiveContent.
class Parent extends React.component {
constructor(props) {
super(props);
this.state = {
activeContent: ''
}
}
setActiveContent(contentId) {
this.setState({
activeContent: this.state.activeContent + contentId
});
}
render(){
<div>
<Content activeContent={this.state.activeContent}/>
<Footer setActiveContent={this.setActiveContent}/>
}
}
export default class Footer extends React.Component {
handleClick() {
this.props.setActiveContent(1);
}
render(){
const button = {
margin: "10em 1em 0 1em"
};
return (
<div className="row">
<div className="col-sm-offset-5 col-sm-2 text-center">
<button className="btn btn-secondary" style={button}>Back</button>
<button onClick={this.handleClick} className="btn btn-success" style={button}>Next</button>
</div>
</div>
)
}
}
export default class Content extends React.Component {
render(){
const Contents = [ <Start />, <Time />, <Information />, <End /> ];
return (
<div className="container">
<div className="row">
{Contents[this.props.activeContent]}
</div>
</div>
);
}
};

Resources