I have a component that basically loads a video in an overlay using JW Player (simplified example below).
import React, { Component } from 'react'
import ReactJWPlayer from 'react-jw-player'
class VideoPopup extends Component {
render() {
return (
<div id="video">
<ReactJWPlayer
playerId='video-player'
playerScript='https://content.jwplatform.com/libraries/vr6ybmGf.js'
file='path to video file'
/>
</div>
)
}
}
export default VideoPopup;
I would like the component to sit directly in the root of my app, but I need to be able to display it when called from ANY other component - this might be a child component, a child of a child, a sibling etc. etc. I was hoping to be able to call it and pass the video file reference simply like below:
<button onClick={somehow show video popup}>show video popup</button>
I understand how to do this easily if there is a direct parent-child relationship, but not if I want to place the link in a variety of different components; I hope someone can point me in the right direction.
If you want to get rid of the parent-children relationships when it comes to actions, use an event manager like Redux (react-redux). It is pretty standard an becomes necessary as your app grows anyway.
The principle is that wherever you place your link, it will fire an "action" on click, which is sent to a global dispatcher that other components listen to for changes.
Multiple ways to do this
You can define a function that controls the show/hide functionality
of the video player in the app component itself and pass it down as a
prop to all the components where the event can be fired.
Use Redux. This is the ideal choice. You just have to dispatch an action from anywhere in your app and the corresponding reducer will take care of the functionality.
Using a global function (not recommended).
Please comment if you need more explanation.
You can try to make global function and call it wherever you want.
showVideoPopup () {
ReactDOM.render(
<VideoPopup />,
document.getElementById('popupHolder')
);
}
Related
I'm working on an Electron project using Svelte for the frontend. I'm relatively new to Svelte. So here is the problem, I have a parent component named MainContent.svelte and two child components editor.svelte and preview.svelte. The Editor and Preview are both placed in the MainContent component. What I want to do is when the content of the Editor is changed, I want to update the Preview pane to reflect those changes. I previously had done this same project using Vanilla JavaScript but thought of using Svelte as it was easier to manage the project.
There is function which listens for any changes in the Editor pane and another function to update the Preview pane. But what I can't wrap my head around is how to call the function to update the Preview when the Editor content changes. Any help would be greatly appreciated.
I would use a property and lift the state up to the main component, you can still use functions internally if you have to, e.g.
<script>
// ...
let content = '...';
</script>
<Editor bind:content />
<Preview {content} />
If the preview needs to update via a function you can call that in a reactive statement:
<script>
// ...
export let content;
$: updatePreview(content);
</script>
I'm trying to embed a tockify calender into a component, within a react project I'm building. I should note that I'm using a library called react-script-tag that allows me to use <script/> tags within my component.
Anyway, the calender is rendering - but then keeps on re-rendering as if it's stuck in some sort of loop. I have a feeling I need to implement some sort of lifecycle method. Any suggestions? Code as follows:
import React from 'react'
import Nav from './Nav'
import ScriptTag from 'react-script-tag'
class Events extends React.Component {
render() {
return (
<div>
<Nav/>
<div data-tockify-component="calendar" data-tockify-calendar="hzevents2"></div>
<ScriptTag isHydrating={false} data-cfasync="false" data-tockify-script="embed" src="https://public.tockify.com/browser/embed.js"></ScriptTag>
</div>
)
}
}
export default Events
Use React.PureComponent instead of React.Component because of React.PureComponent will prevent to re-rendering if there is no need or update.
The documentation of 'react-script-tag' says
It is recommended that the Script tag is placed in a component that
only renders once in the entire life of your app. Otherwise, a new
tag will be appended each time the component mounts again.
There are plans down the road to prevent this.
You may want to use a pure component to prevent rerendering.
Edit: Regardless of my answer you should show the rest of your code to be able to detect the problem.
I've recently started learning React and I'm trying to build a simple audio player. I'm currently using this example as a reference but it's built in one file
https://github.com/CezarLuiz0/react-cl-audio-player
The one I'm trying to make is done in a "React" way where the UI has reusable components but I'm having trouble separating my code into meaningful and working components. For example, if I try to move some of the rendering code from the parent component (AudioPlayer) into (PlayButton), the audio methods that is created on the mounting of the parent component suddenly becomes inaccessible to the child components.
Here is my code repo.
https://github.com/vincentchin/reactmusicplayer
It works now but I'd like to improve it. Also it'd be great if someone can point out huge flaws in this since I'm sure I've broken some rules or standards to coding in React.
You can access parent component's methods from a child component by passing the method as a prop, and then invoking it inside the child component.
For example (in the child component's render method):
<button onClick={this.props.methodFromTheParent}>Click me</button>
You can also pass arguments to these methods:
<button onClick={this.props.methodFromTheParent.bind(null, 'Hello')}>Click me</button>
Remember to pass in null instead of this as the first argument when binding values to a method belonging to a parent component.
I skimmed through your repo as well. You could clean up the AudioPlayer component a lot by putting the different elements into their own components.
The render method could look something like this:
render() {
return (
<div>
<PlayButton onClick={this.togglePlay} playing={this.state.playing} />
{!this.state.hidePlayer ?
(<Player
playerState={this.state}
togglePlay={this.togglePlay}
setProgress={this.setProgress}
...
/>) : null}
</div>
);
}
And then inside the newly-created Player component:
render() {
var pState = this.props.playerState; // Just to make this more readable
return (
<div className="player">
<PlayButton onClick={this.props.togglePlay} playing={pState.playing} />
<Timeline
currentTimeDisplay={pState.currentTimeDisplay}
setProgress={this.props.setProgress}
progress={pState.progress}
...
/>
<VolumeContainer
onMouseLeave={this.props.noShow}
setVolume={this.setVolume}
toggleMute={this.toggleMute}
...
/>
</div>
);
}
You can break the layout into as many nested components as is needed and makes sense.
Remember to actually add the onClick attribute inside the child components as well (<button onClick={this.props.onClick}>Play</button>).
folks. I'm a relatively new Meteor developer, and after learning Blaze, I decided to start learning React, because it seemed like the right thing to do, and I sort of liked the idea of how it worked.
Anyway, I'm having issues with a bit of code I'm working on, and could use some guidance... I've got the following segments of code:
https://gist.github.com/czbaker/2101526219eea5330553
For some reason, when the form in the component is submitted, it isn't firing the function that's meant to handle submission. Instead, it refreshes the page (as event.preventDefault() never happens).
What would be causing this to happen? As per suggested on IRC, I've tried replacing onSubmit={this.handleSubmit} with the following:
onSubmit={()=>{this.handleSubmit}}
onSubmit={this.handleSubmit()}
Neither of them had any effect, the form submission function still isn't being called. I'm really confused, because I followed documentation for the most part, and it looks like it should be working.
As I'm really new to React, I'm sure I'm overlooking something, but have no idea what. Can anyone offer me some aid? Thanks in advance!
The current project is also in a BitBucket repository, for those who need it https://bitbucket.org/czbaker/karuto/
All I've been able to figure out so far is that if I render the problem component by itself (not as a child of another component) using ReactLayout, it works fine, but the second that I try to render it as a child component (doing it the way it's shown in MDG's Todos tutorial (React version), events refuse to fire, yet there's no errors.
Thanks in advance for help.
The problem is you are attempting to render the entire HTML tree using React.
If you are using flow-router and react-layout, you can simply render the document fragment that you desire and it will be placed in a designated root node which id is 'react-root'.
Otherwise, I would suggest using static-html if you don't need blaze and create a root element for React:
some_file.html:
<body>
<div id="react-container"></div>
</body>
and then render the root component into it using your preferred router.
Then, change the title dynamically via a ReactiveVar or some other method.
I am using React with React Router, alongside Google's MDL, and had the same issue (as well as a few others, such as navigating to different routes would cause a full page reload).
When attempting to find the cause, I found that removing the MDL classes from the the div surrounding {this.props.children} in my parent component resulted in the event listeners firing correctly.
After investigating, it appears that this is due to the way that MDL structures the DOM nodes, and can be resolved by either calling componentHandler.upgradeDOM() in each child component's componentDidUpdate() method, as follows:
App = React.createClass({
render() {
return(
<div className="mdl-layout mdl-js-layout">
...
<div className="mdl-layout__content">
{ this.props.children }
</div>
</div>
);
}
});
ChildComponent = React.createClass({
handleClick() {
alert("I've been clicked!");
},
render() {
return(
<div>
<button onClick={this.handleClick}
className="mdl-button mdl-js-button">Click Me</button>
</div>
);
},
componentDidUpdate() {
componentHandler.upgradeDOM();
},
});
As outlined here, http://quaintous.com/2015/07/09/react-components-with-mdl/; or by using a 'patched' version of MDL, like the one here:
https://github.com/tleunen/react-mdl
I know this is a little different to the OP's issue, but I figured I'd add this here in the hopes that it helps someone else with this issue. :)
In my Layout.jsx i changed
export default class extends React.Component {
render() {
return(
<body>
...some jsx
</body>
);
}
}
to
export default class extends React.Component {
render() {
return(
<div>
...some jsx
</div>
);
}
}
and it helps, now the React events are working fine
Before anyone press eagerly the close button, I already have looked the following question: ReactJS Two components communicating. My problem is exactly the third scenario developped in the current accepted answer.
I am using ReactJS to build something with two components. For HTML reasons (and presentation), i want my two components to be at two different places of the page.
For the moment, I have the following pattern, corresponding to scenario #2:
FooForm = React.createClass({
...
});
FooList = React.createClass({
...
});
FooManager = React.createClass({
...
render: function () {
return (
<div>
<FooForm ref="form" manager={this} />
<FooList ref="list" />
</div>
);
}
});
React.render(
<FooManager someProp={value} />,
document.getElementById('foo')
);
This gives something like:
<div id="foo">
<form>Form generated with the render of FooForm</form>
<ul>List generated with the render of FooList</ul>
</div>
However, i would like to have something like this:
<div id="fooform">
<form>Form generated with the render of FooForm</form>
</div>
<!-- Some HTML + other controls. Whatever I want in fact -->
<div>...</div>
<div id="foolist">
<ul>List generated with the render of FooList</ul>
</div>
The problem here is: how can I keep a reference in each component? Or at least the link Form -> List?
I tried to create the FooList before and pass the reference to the current manager, but I get the following warning/error:
Error: Invariant Violation: addComponentAsRefTo(...): Only a ReactOwner can have refs. This usually means that you're trying to add a ref to a component that doesn't have an owner (that is, was not created inside of another component's `render` method). Try rendering this component inside of a new top-level component which will hold the ref.
The documentation says you can attach events to link two components which do not have a parent-child relation. But I don't see how. Can someone give me some pointers?
The Less Simple Communication lesson from react-training has a good example of how you can move actions & state sideways to avoid having to create an explicit link between related components.
You don't need to jump into a full Flux implementation to get the benefit of this approach, but it's a good example to lead you up to Flux, should you eventually need it or something like it.
Note that this requires you to model the relationship between the components based on changing state rather than explicitly passing a reference to a component instance (as you're doing above) or a callback bound to the component managing the state.
This would be the perfect use-case for a Flux type architecture.
What you want is someone FooManager to be able to trigger state changes in both components. Or, in fact, having the different components trigger, through Actions, state changes in each other.
The Flux Todo-App Tutorial illustrates your use-case perfectly!
After this, then you'd have the choices of using Facebooks implementation of Flux or the other gazillion ones.
My personal favorite is Reflux