I have the following code in reactjs:
How can I access any component like jquery using ID or there is some other solution?
Progressbar.jsx
class Progressbar extends Component {
constructor() {
super();
this.state = { percentage: 0 };
}
progress(percentage) {
this.setState({ percentage:percentage });
}
render() {
// using this.state.percentage for the width css property
........
........
}
};
FileUploader.jsx
class FileUploader extends Component {
onUploadProgress() {
// I want to call Progress1.progress( percentage_value )
}
render() {
........
<Progressbar id="Progress1" />
........
........
}
};
You are not thinking about the structure correctly. Progressbar does not need to maintain state at all:
class Progressbar extends Component {
render() {
// using this.props.percentage for the width css property
}
};
Keep state in FileUploader and pass it down to progress bar.
class FileUploader extends Component {
constructor() {
super();
this.state = { percentage: 0 };
}
onUploadProgress(percentage) {
this.setState({ percentage: percentage });
}
render() {
...
<Progressbar percentage={this.state.percentage} />
}
};
ProgressBar can be a stateless component.
The value of ProgressBar is updated from the state of its parent.
There is a demo:
const ProgressBar = props => (
<div style={{ display: 'block', width: `${props.percentage}%`, height: 20, backgroundColor: '#ccc' }} />
);
class App extends Component {
state = {
percentage: 0,
}
componentDidMount() {
setInterval(() => {
let nextPercent = this.state.percentage+1;
if (nextPercent >= 100) {
nextPercent = 0;
}
this.setState({ percentage: nextPercent });
}, 100);
}
render() {
return (
<div>
<h2>Progress bar</h2>
<ProgressBar percentage={this.state.percentage} />
</div>
);
}
}
There is the fully functional codesandbox demo https://codesandbox.io/s/6y9p69q0xw
Related
I have two class components: Title and PlayButton. By default, the Title is programmed to change images when it is being hovered over but I would like it so that when the Playbutton is hovered over, the Title also changes its image (changes the state of the image). How would I go about this? I know I should use a parent component that handles the state of both its "children" (the Title and the PlayButton), but since I'm new to react, I'm not sure how.
Any assistance would be appreciated, thank you!
Code for Title:
import React from 'react'
import './Title.css'
import playHoverProvider from './playHoverProvider'
class Title extends React.Component {
constructor(props) {
super(props);
this.state = {
imgSrc: require('./oglogo'),
control: require('./oglogo')
};
this.handleMouseOver = this.handleMouseOver.bind(this);
this.handleMouseOut = this.handleMouseOut.bind(this);
}
handleMouseOver() {
this.setState({
imgSrc: require('./difflogo')
});
}
handleMouseOut() {
this.setState({
imgSrc: require('./oglogo')
});
}
render() {
return (
<div className='logo'>
<view>
<img onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut} src={this.state.imgSrc}
style={{width: 800,
flex: 1,
height: null,
}}
alt = 'Logo' />
</view>
</div>
);
}
}
Title.propTypes = {
}
Title.defaultProps = {
}
export default Title;
Code for PlayButton:
import { hover } from '#testing-library/user-event/dist/hover';
import React from 'react'
import './PlayButton.css';
class PlayButton extends React.Component {
constructor(props) {
super(props);
this.state = {
imgSrc: require('./playbutton.png'),
disabled: false
};
this.handleMouseOver = this.handleMouseOver.bind(this);
this.handleMouseOut = this.handleMouseOut.bind(this);
}
// On-click
handleClick = (event) => {
if (this.state.disabled) {
return;
}
this.setState({
disabled: true
});
}
handleMouseOver () {
this.setState({
imgSrc: require('./playbuttonblue.png')
});
}
handleMouseOut () {
this.setState({
imgSrc: require('./playbutton.png')
});
}
render() {
return (
<div className='playbutton'>
<a href='./Rule'>
<button className='buttonprop' onClick={this.handleClick} disabled={this.state.disabled}>
{this.state.disabled ? '' :
<img onMouseOver={this.handleMouseOver}
onMouseOut={this.handleMouseOut}
src={this.state.imgSrc} width = {100} height = {50} alt = 'Play'/>}
</button>
</a>
</div>
);
}
}
PlayButton.propTypes = {
}
PlayButton.defaultProps = {
}
export default PlayButton
I'am getting props from child in getCount function. And set it prop into state. Than i try set it in component and get infinity loop. How can i fix that?
There is code of parent component:
import React, { Component } from "react";
import Message from "./Message/Message";
export default class Widget extends React.Component {
constructor(props) {
super(props);
this.state = {
color: {
s: 30,
l: 60,
a: 1
},
counter: 0
};
}
getCount = count => this.setState(state => ({
counter: count
}));
getColor = color => {
console.log(`the color is ${color}`);
};
render() {
const counter = this.state.counter;
return (
<div>
<Message
getColor={this.getColor}
getCount={this.getCount}
color={this.state.color}
>
{undefined || `Hello World!`}
</Message>
{counter}
</div>
);
}
}
child:
import React, { Component } from "react";
export default class Message extends React.Component {
constructor(props) {
super(props);
this.changeColor = this.changeColor.bind(this);
this.state = { h: 0 };
this.counter = 0;
}
changeColor = () => {
this.setState(state => ({
h: Math.random()
}));
};
componentDidUpdate(prevProps) {
this.props.getColor(this.color);
this.props.getCount(this.counter);
}
render() {
this.counter++;
const { children } = this.props;
const { s, l, a } = this.props.color;
this.color = `hsla(${this.state.h}, ${s}%, ${l}%, ${a})`;
return (
<p
className="Message"
onClick={this.changeColor}
style={{ color: this.color }}
>
{children}
</p>
);
}
}
The problem lies in your Message component.
You are using getCount() inside your componentDidUpdate() method. This causes your parent to re-render, and in turn your Message component to re-render. Each re-render triggers another re-render and the loop never stops.
You probably want to add a check to only run the function if the props have changed. Something like:
componentDidUpdate(prevProps) {
if(prevProps.color !== this.props.color) {
this.props.getColor(this.color);
this.props.getCount(this.counter);
}
}
This will keep the functionality you need, but prevent, not only the infinity-loop, but also unnecessary updates.
If I try to change label button I got an error because label is a read only property.
How could I change button text dinamically?
export default class Tagger extends Component {
static propTypes = {
name: PropTypes.string
}
constructor(props) {
super(props)
this.state = {
disabled: true
}
this.enableEdit = this.enableEdit.bind(this)
}
componentDidMount() {
this.editButton = React.findDOMNode(this.refs.editButton)
}
enableEdit() {
this.setState({disabled: !this.state.disabled})
this.refs.editButton.props.label = 'Save'
}
render() {
return (
<div>
<RaisedButton onClick={this.enableEdit} label='Modify' primary={true} ref='editButton' />
</div>
)
}
}
Props are read-only , you can't mutate/edit them
You can simply change the props instead of mutating them. Set the value of the prop to state and simply pass it.
export default class Tagger extends Component {
static propTypes = {
name: PropTypes.string,
}
constructor(props) {
super(props)
this.state = {
disabled: true,
label = "Modify" // initial state
}
this.enableEdit = this.enableEdit.bind(this)
}
componentDidMount() {
this.editButton = React.findDOMNode(this.refs.editButton)
}
enableEdit() {
this.setState({
disabled: !this.state.disabled,
label:"Save" // update it here
})
}
render() {
// take value from state and pass it, no need for ref
return (
<div>
<RaisedButton onClick={this.enableEdit} label={this.state.label} primary={true} />
</div>
)
}
}
You should use state instead of refs.
export default class Tagger extends Component {
constructor(props) {
super(props)
this.state = {
disabled: true,
label: 'Modify'
}
this.enableEdit = this.enableEdit.bind(this);
}
enableEdit() {
this.setState({disabled: !this.state.disabled, label: 'Save'});
}
render() {
return (
<div>
<RaisedButton onClick={this.enableEdit} label={this.state.label} primary={true} />
</div>
)
}
}
I'm using Clappr player with ReactJS.
I want Clappr player component appear and destroy when I click to a toggle button. But it seems like when Clappr player is created, the entire page has reload (the toggle button dissapear and appear in a blink). So here is my code:
ClapprComponent.js
import React, { Component } from 'react'
import Clappr from 'clappr'
class ClapprComponent extends Component {
shouldComponentUpdate(nextProps) {
let changed = (nextProps.source != this.props.source)
if (changed) {
this.change(nextProps.source)
}
return false
}
componentDidMount() {
this.change(this.props.source)
}
componentWillUnmount() {
this.destroyPlayer()
}
destroyPlayer = () => {
if (this.player) {
this.player.destroy()
}
this.player = null
}
change = source => {
if (this.player) {
this.player.load(source)
return
}
const { id, width, height } = this.props
this.player = new Clappr.Player({
baseUrl: "/assets/clappr",
parent: `#${id}`,
source: source,
autoPlay: true,
width: width,
height: height
})
}
render() {
const { id } = this.props
return (
<div id={id}></div>
)
}
}
export default ClapprComponent
Video.js
import React, { Component } from 'react'
import { Clappr } from '../components'
class VideoList extends Component {
constructor() {
super()
this.state = {
isActive: false
}
}
toggle() {
this.setState({
isActive: !this.state.isActive
})
}
render() {
const boxStyle = {
width: "640",
height: "360",
border: "2px solid",
margin: "0 auto"
}
return (
<div>
<div style={boxStyle}>
{this.state.isActive ?
<Clappr
id="video"
source="http://qthttp.apple.com.edgesuite.net/1010qwoeiuryfg/sl.m3u8"
width="640"
height="360" />
: ''}
</div>
<button class="btn btn-primary" onClick={this.toggle.bind(this)}>Toggle</button>
</div>
)
}
}
export default VideoList
Anyone can explain why? And how to fix this problem?
Edit 1: I kind of understand why the button is reload. It's because in index.html <head>, I load some css. When the page is re-render, it load the css first, and then execute my app.min.js. The button doesn't reload in a blink if I move the css tags under the <script src="app.min.js"></script>.
But it doesn't solve my problem yet. Because the css files have to put in <head> tags. Any help? :(
Here you have a running (jsbin link) example. I simplified a little bit and it still shows your main requirement:
class ClapprComponent extends React.Component {
componentDidMount(){
const { id, source } = this.props;
this.clappr_player = new Clappr.Player({
parent: `#${id}`,
source: source
});
}
componentWillUnmount() {
this.clappr_player.destroy();
this.clappr_player = null;
}
render() {
const { id } = this.props;
return (
<div>
<p>Active</p>
<p id={id}></p>
</div>
);
}
}
class NotActive extends React.Component {
render() {
return (
<p>Not Active</p>
);
}
}
class App extends React.Component {
constructor(props){
super(props);
this.toggle = this.toggle.bind(this);
this.state = {
isActive: false
}
}
toggle() {
this.setState({
isActive: !this.state.isActive
})
}
render() {
return (
<div>
<h1>Clappr React Demo</h1>
{ this.state.isActive ?
<ClapprComponent
id="video"
source="http://www.html5videoplayer.net/videos/toystory.mp4"
/> :
<NotActive />}
<button onClick={this.toggle}>Toggle</button>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
Also make sure to rename the button class property to className.
Maybe you can work from here to find your exact problem? Hope that helps.
In Clappr's documentation I found a like about how to use clappr with reactjs
I wish to add the checks done (once the component mounts in CDM) to detect userAgent - for the purposes of mobile/flash/touchDevice detections to context rather than to the state. Is this possible? if so how would you do that? I am currently getting undefined when I attempt to access the value fo the context for the isFlashInstalled. Here is glimpse into the component setting the context:
App.js
export class App extends Component {
static childContextTypes = {
isFlashInstalled: React.PropTypes.bool
};
constructor() {
super();
this.state = {
isFlashInstalled: false
};
}
getChildContext() {
return {
isFlashInstalled: this.state.isFlashInstalled
};
}
componentDidMount() {
const flashVersion = require('../../../client/utils/detectFlash')();
// I know this could be done cleaner, focusing on how for now.
if (flashVersion && flashVersion.major !== 0) {
this.setFlashInstalled(true);
} else {
this.setFlashInstalled(false);
}
}
setFlashInstalled(status) {
this.setState({isFlashInstalled: status});
}
}
Later when trying to access isFlashInstalled from context I will get undefined
ChildComponent.js
export class ChildComponent extends Component {
// all the good stuff before render
render() {
const {isFlashInstalled} = this.context
console.log(isFlashInstalled); // undefined
}
}
did you correctly set up context types for parent and child? I did a test and it works, see the componentDidMount that set the state asynchronously:
class Parent extends React.Component {
state = {
color: 'red'
}
getChildContext() {
return {
color: this.state.color
};
}
componentDidMount() {
setTimeout(() => this.setState({color: 'blue'}), 2000)
}
render() {
return (
<div>Test <Button>Click</Button></div>
);
}
}
Parent.childContextTypes = {
color: React.PropTypes.string
}
class Button extends React.Component {
render() {
return (
<button style={{background: this.context.color}}>
{this.props.children}
</button>
);
}
}
Button.contextTypes = {
color: React.PropTypes.string
};
http://jsbin.com/cogikibifu/1/edit?js,output