Changing background-image after onClick event in ReactJS - reactjs

I have a component, let's say it looks like this:
return(
<div id="container"></div>
)
From the beginning it's background image is already set, let's say
#container{
background-image: url('./assets/container.jpg');}
Now I want to add another element inside the container div that will have onClick event firing function, that will do other things + changing the parent's background-image. Dummy code would be:
handleOnClick(){
doOtherStuff();
document.getElementById('container').style.backgroundImage = "url('./assets/container2.jpg')"}
return(
<div id="container">
<div onClick={()=> handleOnClick()}
</div>
)
Problems are:
It doesn't work, it changes background to blank screen,
After I leave the component and go back to it, it reverts to the old background. Is there any way to avoid that without having the background linked with state? I already have a lot of things in store and it will start to get messy real soon if I start adding more styles to it.

This is doable by giving your child the ability to change the state in your parent and holding that logic there. Also, it's much better to control your background shift by using this.setState. Just make it a boolean that controls which CSS id to use.
class Parent extends Component {
constructor() {
super()
this.state = {
defaultBackground: true
}
}
toggleChildBackground() {
const newBackground = !this.state.defaultBackground
this.setState({defaultBackground: newBackground})
}
render() {
return (
<div>
<Child
defaultBackground={this.state.defaultBackground}
toggleChildBackground={this.toggleChildBackground.bind(this)}
/>
</div>
)
}
}
class Child extends Component {
handleClick() {
this.props.toggleChildBackground()
}
render() {
return (
<div id={this.props.defaultBackground ? 'id1' : 'id2'}>
<button onClick={this.handleClick.bind(this)}>
change background
</button>
</div>
)
}
}

Related

How can I call event.target.playVideo() using npm 'react-youtube' when a parent state changes without re-rendering child?

I feel like this is a simple issue caused by my lack of experience with react, forgive me if I've over-complicated it.
Using npm 'react-youtube', I am trying to call event.target.pauseVideo() on the Video component whenever a specific parent state changes. I have a button which toggles the video component's div display to 'none' or 'block' when clicked, I want this button to also pause or play the video without having to re-render the Video component. I can call event.target.pauseVideo() inside the provided _onReady(){} or _onStateChange(){} with no issues, however I want it to be triggered when I press a button, not on either of those triggers.
I've tried creating my own method in the Video component which calls event.target.pauseVideo(), however I do not know how to trigger this from the parent (I think this is doable using refs, but everywhere I read says refs should not be used 99% of the time.) I've also tried passing event.target.pauseVideo() to the component from the parent, but this seems to try to use an event or target from the parent which doesn't exist instead of the Video component's.
Parent:
class Window extends React.Component {
constructor(props){
super(props)
this.state={
currentDisplay: 1,
isPaused: 0,
}
}
renderVideo(isPaused){
return <Video isPaused={isPaused}>;
}
render() {
return (
<div className='window'>
<div className = 'vid'>
{this.renderVideo(this.state.isPaused)}
</div>
<div className = 'PauseButton'>
<Pause handleClick={() => {this.togglePause()}}/>
</div>
</div>
);
}
}
togglePause() just changes the pause state from 0 to 1 and vice versa
Pause button Component:
class PauseButton extends React.Component{
render(){
return(
<button onClick={() => {this.props.handleClick()} }>
Pause
</button>
)
}
}
Video Component:
class Video extends React.Component{
_pause(event, isPaused){
if(isPaused = 1){event.target.pauseVideo()}
}
_onReady(event) {
//event.target.pauseVideo()
}
render(){
const opts = {
height: '390',
width: '640',
playerVars: { // https://developers.google.com/youtube/player_parameters
autoplay: 1,
}
}
return(
<YouTube
videoId= {'21X5lGlDOfg'}
opts={opts}
onReady={this._onReady}
onStateChange={this._onStateChange}
/>
)
}
}
Currently, the '_pause()' method in the video component is never triggered, but I would like it to be triggered whenever the pause button is clicked.
The code above plays the video without issues, however I am looking for a way to trigger the _pause() method in the Video component without having to re render the component.
Created a code sandbox with Video gettign paused when clicking pause button. Achieved using React Hooks.
https://codesandbox.io/s/elegant-cerf-0os7t

Is it okay to call setState on a child component in React?

I have some text. When you click on that element a modal pops up that lets you edit that text. The easiest way to make this work is to call setState on the child to initialise the text.
The other way, although more awkward, is to create an initial text property and make the child set it's text based on this.
Is there anything wrong with directly calling setState on the child or should I use the second method?
Although it is recommended to keep the data of your react application "up" in the react dom (see more here https://reactjs.org/docs/lifting-state-up.html), I don't see anything wrong with the first aproach you mentioned.
If you have to store data that is very specific of a child I don't see anything wrong in keep that information in the child's state.
It seems that your modal doesn't need to have its own state, in which case you should use a stateless React component.
This is one way of passing the data around your app in the React way.
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
initialText: "hello",
}
this.saveChildState = this.saveChildState.bind(this);
}
saveChildState(input) {
console.log(input);
// handle the input returned from child
}
render() {
return (
<div>
<ChildComponent
initialText={this.state.initialText}
save={this.saveChildState}
/>
</div>
);
}
}
function ChildComponent(props) {
return (
<div>
<input id="textInput" type="text" defaultValue={props.initialText}>
</input>
<button onClick={() => props.save(document.getElementById('textInput').value)}>
Save
</button>
</div>
)
}
Maybe I am misinterpreting your question, but I think it would make the most sense to keep the modal text always ready in your state. When you decide to show your modal, the text can just be passed into the modal.
class Test extends Component {
constructor() {
this.state = {
modalText: 'default text',
showModal: false
}
}
//Include some method to change the modal text
showModal() {
this.setState({showModal: true})
}
render(
return (
<div>
<button onClick={() => this.showModal()}>
Show Modal
</button>
{ this.state.showModal ? <Modal text={this.state.modalText}/> : null }
</div>
)
)
}

the changed value does not show in Dom

I am learning React, in my test app, I make 2 identical sets of random colored array that will shuffle and change color every time I click on 'change color' button. however I can not seem to make the Dom updates my array colors even the values of colors change correctly.
import React from 'react';
class Card extends React.Component{
constructor(props){
super(props);
const {r,g,b}=this.props.card
this.state={
style:{
width:'100px',
height:'100px',
display:'inline-block',
backgroundColor:`rgb(${r},${g},${b})`
}
}
}
onClick=()=>{
const {r,g,b}=this.props.card
console.log('color values of the card with index',this.props.id ,' is: ', r,g,b)
}
render(){
const {style}=this.state
return (
<div style={style}>
<button onClick={this.onClick}>card test</button>
</div>
)
}
}
export default Card;
this is the picture of my problem
as you can see in the picture, the values change every time I click but the cards' color stay the same. however, it will work if I change the class based component into non class based component and set the style in render() instead of constructor, but I want to have a class component so I can pass the card I click to parent component.
Is onClick triggering something else as well? Otherwise, don't see what would change the card's values, since the onClick is just logging.
Assuming the card prop is somehow changing correctly, I believe your issue is that your card prop is updating, but the state is set in the constructor and never updated.
Rather then setting a style value in state, I would just change to calculating style in render.
render() {
const {r,g,b} = this.props.card
const style = {
width: '100px',
height: '100px',
display: 'inline-block',
backgroundColor: `rgb(${r},${g},${b})`
}
return (
<div style={style}>
<button onClick={this.onClick}>card test</button>
</div>
)
}
You generally do not what to keep state that is easily derived from props to avoid situations like this.

In React, Is it good practice to search for certain element in DOM?

Is it good to just specify className for element so i could find it later in the DOM through getElementsByClassName for manipulations?
Adding a class to find the DOM element? Sure you can do that, but refs are probably the better solution.
Manipulating the DOM element? That's an absolute no-go. The part of the DOM that is managed by React should not be manipulated my anything else but React itself.
If you come from jQuery background, or something similar, you will have the tendency to manipulate element directly as such:
<div class="notification">You have an error</div>
.notification {
display: none;
color: red;
}
.show {
display: block;
}
handleButtonClick(e) {
$('.notification').addClass('show');
}
In React, you achieve this by declaring what your elements (components) should do in different states of the app.
const Notification = ({ error }) => {
return error
? <div className="notification">You have an error</div>
: null;
}
class Parent extends React.Component {
state = { error: false };
render() {
return (
<div>
<Notification error={this.state.error} />
<button onClick={() => this.setState({ error: true })}>
Click Me
</button>
}
}
The code above isn't tested, but should give you the general idea.
By default, the state of error in Parent is false. In that state, Notification will not render anything. If the button is clicked, error will be true. In that state, Notification will render the div.
Try to think declaratively instead of imperatively.
Hope that helps.
When using React, you should think about how you can use state to control how components render. this.setState performs a rerender, which means you can control how elements are rendered by changing this.state. Here's a small example. I use this.state.show as a boolean to change the opacity of the HTML element.
constructor(props) {
super(props)
this.state = {
show: true
}
}
handleClick() {
this.setState({show: false})
}
render() {
const visibility = this.state.show ? 1 : 0
return (
<button style={{opacity: visibility} onClick={() => this.handleClick()}>
Click to make this button invisible
</button>
)
}

i am not able retrieve array elements one at a time if i call them in my component all the elements are retrieved at a time

I want to load my array element when an event is occurred by referencing the key i tried different variables for the key but it would not accept all the elements of the array are being displayed if i give index as the key.
I am new to Reactjs and not very familiar with all the syntax and concept can somebody help me with the logic to solve this.
The event I am triggering is onClick or onChange.
`var Qstn =['A','B','C','D','E','F','G','H','I','J'];
<div>
{Qstn.map(function(Q,index){
return <span className="col-md-4 txt" key={index}>{Q}</span>
})}
</div>`
Ok I made a codepen with an example
http://codepen.io/lucaskatayama/pen/QGGwKR
It's using ES6 classes components, but it's easy to translate.
You need to set initial state to an empty array like [].
On click button, it call onClick() method which uses this.setState({}) to change component state.
When React notice state changes, it re-render the component.
class Hello extends React.Component {
constructor(){
super();
//Initial State
this.state = {
Qstn : []
}
}
onClick(){
//Called on click button
// Set state to whatever you want
this.setState({Qstn : ['A','B','C','D','E','F','G','H','I','J']})
}
render(){
let Qstn = this.state.Qstn; // load state and render
return (
<div>
<button onClick={() => this.onClick()}>Click</button>
<div>
{Qstn.map(function(Q,index){
return <span className="col-md-4 txt" key={index}>{Q}</span>
})}
</div>
</div>
)
}
}
ReactDOM.render(<Hello />, document.getElementById('container'))

Resources