Using WaveSurfer.js v2 in React Component - javascript

I'm trying to use WaveSurfer.js(version 2) with React.
I know that for version 1 there was a library called react-wavesurfer, but I really want to do it with v2.
I'm already experienced with it, without React. So, I just made a component of mine.
As you can see in the code below, it all works perfectly, the WaveSurfer object is generated correctly in the componentDidMount() but then, all of sudden, in the load() method, there's an error I can't understand correctly.
This error is throwed after the load(), and after the error, it's logged in console "There it happens!" and the wave property of the state, as it should be.
This is the error in question and by itself it doesn't mean nothing. That's just an error without stack trace in a obfuscated function in react-error-overlay. The second error, DOMException, is directly caused by the first, and they everytime are throwed together.
This is the row of the react-error-overlay that causes the error directly, in /node_modules/react-error-overlay/index.js:1582, but it's obfuscated.
// ... various imports
class Track extends Component {
wavref = null;
constructor(props) {
super(props);
let id = this.props.id;
this.state = {
id: id,
wave: null
};
}
load() {
console.log(this.state.wave, this.props.audio); // this.props.audio is the correct path, and should work correctly: "../../demo.wav".
this.state.wave.load(this.props.audio);
console.log("There it happens!", this.state.wave);
}
componentDidMount() {
let generatedWave = WaveSurfer.create({
container: ReactDOM.findDOMNode(this.waveref),
waveColor: this.state.color,
progressColor: this.state.progressColor,
plugins: [
RegionsPlugin.create({
dragSelection: {
slop: 5
}
}),
CursorPlugin.create({})
]
});
this.setState({
wave: generatedWave,
}, function() {
this.load();
});
}
render() {
return(
<div ref={(waveref) => this.waveref = waveref}></div>
);
}
}
export default Track;
Obviously, the load() method doesn't load/render the actual wave in the WaveSurfer canvas, and throws that error. I can't understand why, because it should just work normally and render the wave in the WaveSurfer canvas.
Does anyone of you know what can the error be?

Related

How do you use functions in a different js file in phaser 3?

Im trying to create a js file with functions relevant to my game but i get an error message saying
Uncaught TypeError: Cannot read properties of undefined (reading 'add')
when i try to use phaser functions outside of the main file.
I have 2 files one called game.js and one called test.js. I am also using the matter physics engine.
game.js :
class bootScene extends Phaser.Scene {
//Preloading assets for later use
preload() {
this.load.image('linus', 'assets/linus.png');
this.load.script('t','test.js')
}
create() { }
update() {
//launch game scene
this.scene.launch('game').stop();
}
}
class playScene extends Phaser.Scene {
constructor() {
super('game');
}
create() {
test('test',1,1,'linus');
}
update() { }
}
// set the configuration of the game
let config = {
type: Phaser.WEBGL, // Phaser will use WebGL if available, if not it will use Canvas
width: 1280,
height: 720,
pixelArt: true,
transparent: false,
autoCenter: true,
backgroundColor: '#000000',
physics: {
default: 'matter',
matter: {
restingThresh: 2,
// debug: {
// renderFill: false
// },
gravity: {
y: 0
}
}
},
scene: [bootScene, playScene]
};
// create a new game, pass the configuration
let game = new Phaser.Game(config);
test.js:
function test(msg,x,y,texture){
console.log(msg)
this.matter.add.image(x,y,texture)
}
I have tried trying to put t.test(etc.) and adding the load script in preload. I tried looking up examples, but I couldn't find any.
Sorry if this is a really obvious fix or im just terrible
You just need to pass the current scene, as a parameter, to the test function, and you will can access the matter object and other properties / functions, from the scene.
function test(scene, msg, x, y, texture) {
console.log(msg);
scene.matter.add.image(x, y, texture);
}
And the function test would have to be called, with the "current scene" in the case it is this:
...
create() {
...
test(this, 'test', 1, 1, 'linus');
...
}
This example of the offical website: Example Script Loading, illustrates this fact indirect. Im that example the scene is not passed as paramter, but the canvas and the context are.
So following this example, passing the scene should solve your problem.
Side Note: In general, if there is no specific reason, to load scripts from inside of a phaser application (as shown above), I would load the scripts within the html-file or use a web-bundler like webpack or others. Not only for performance/minification reason.

How to correctly get user information in React/Redux/OIDC

Fairly new to OIDC - I've done a bit of work with it in the past, but I wouldn't call myself an expert by any means.
I am trying to use OIDC in a react app using the oidc-client-js and redux-oidc libraries (along with the redux-oidc-example as an example)
I'm getting this error:
Error: No matching state found in storage
at eval (oidc-client.min.js:1011)
Now I've looked around for a solution to this, and this is the closest I'm seeing to my problem, but I still don't find anything here clearly explaining anything:
https://github.com/IdentityModel/oidc-client-js/issues/648
Here's my callback function:
const successCallback = (user) => {
this.props.dispatch(push("/settings"))
};
class CallbackPage extends React.Component {
render() {
return (
<CallbackComponent
userManager={userManager}
successCallback={successCallback}
errorCallback={error => {
//this.props.dispatch(push("/chat"));
console.error(error);
}}
>
<div>Redirecting TEST...</div>
</CallbackComponent>
);
}
}
export default connect()(CallbackPage);
Before commenting out the props.dispatch in the error callback, it was a never-ending loop. Now it just freezes on the callback page with whatever I put in the error callback.
I won't put my full userManager settings, as that seems like it would be excessive, but here are some of the main highlights:
{
"authority": "https://subdomain.appname.com/auth/realms/appname/",
"client_id": "appname-app",
"redirect_uri": "http://localhost:3001/callback",
"response_type": "code",
"extraQueryParams": {"kc_idp_hint": "google"},
"loadUserInfo": true,
"scope": ["openid", "profile", "email"]
}
I'm not sure what else would be helpful to anyone troubleshooting it - I can give my store.js information or my main application index.js if needed.
Scope paramenter for user meneger should be space separated string and not an array.
Example: 'openid profile email'
Try this.
class CallbackPage extends React.Component {
successCallback = (user) => {
this.props.dispatch(push("/settings"))
};
render() {
return (
<CallbackComponent
userManager={userManager}
successCallback={this.successCallback}
errorCallback={error => {
//this.props.dispatch(push("/chat"));
console.error(error);
}}
>
<div>Redirecting TEST...</div>
</CallbackComponent>
);
}
}
export default connect()(CallbackPage);

Javascript / React window.onerror fired twice

I want to globally catch errors in my React application.
But every time the error is caught/forwarded twice to my registered function.
Example code:
window.onerror = (msg, url, lineNo, columnNo, error) => {
console.log(msg)
alert(msg)
}
class TodoApp extends React.Component {
constructor(props) {
super(props)
}
render() {
return (
<button onClick={(e)=>{
console.log("clicked")
null.bla
}}>
Create an error
</button>
)
}
}
ReactDOM.render(<TodoApp />, document.querySelector("#app"))
Here is a JS-fiddle:
https://jsfiddle.net/dmxur0rc/4/
The console only shows one 'clicked' log, so it's not the button that fires twice, but the error event.
It is known react error, related with implementation of error boundaries.
I found a basic solution to this that should work in all scenarios.
It turns out that the object is identical in all calls, you could set up something to match them exactly, or you could just attach a custom attribute to the error object...
Admittedly this may only work with window.addEventListener('error', function...), as you are given the genuine error object as an argument, as opposed to window.onerror = function... which gets the data parts, such as message & lineNumber as opposed to the real error.
This is basically how I'm using it:
window.addEventListener('error', function (event) {
if (event.error.hasBeenCaught !== undefined){
return false
}
event.error.hasBeenCaught = true
// ... your useful code here
})
If this is called with the same error twice it will exit before getting to your useful code, only executing the useful code once per error.
You need to return true from your error handler otherwise the default error handler will fire:
https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror
When the function returns true, this prevents the firing of the default event handler.
Also note that other error handlers may be in place via addEventHandler.
As mentioned in other answers, the problem is in React in DEV mode. In this mode it re-throws all exceptions to "improve debugging experience".
I see 4 different error scenarios
Normal JS errors (for example, from an event handler, like in the question).
These are sent to window.onerror twice by React's invokeGuardedCallbackDev.
JS errors that happen during render, and there is no React's error boundary in the components tree.
The same as scenario 1.
JS errors that happen during render, and there is an error boundary somewhere in the components tree.
These are sent to window.onerror once by invokeGuardedCallbackDev, but are also caught by the error boundary's componentDidCatch.
JS errors inside promises, that were not handled.
These aren't sent to window.onerror, but rather to window.onunhandledrejection. And that happens only once, so no problem with this.
My workaround
window.addEventListener('error', function (event) {
const { error } = event;
// Skip the first error, it is always irrelevant in the DEV mode.
if (error.stack?.indexOf('invokeGuardedCallbackDev') >= 0 && !error.alreadySeen) {
error.alreadySeen = true;
event.preventDefault();
return;
}
// Normal error handling.
}, { capture: true });
I use this error boundary to handle both React and global errors. Here is some advice from the React documentation:
Error boundaries are React components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of the component tree that crashed.
A class component becomes an error boundary if it defines either (or
both) of the lifecycle methods static getDerivedStateFromError() or
componentDidCatch().
Only use error boundaries for recovering from unexpected exceptions;
don’t try to use them for control flow.
Note that error boundaries only catch errors in the components below them in the tree; An error boundary can’t catch an error within itself.
class ErrorBoundary extends React.Component {
state = {
error: null,
};
lastError = null;
// This lifecycle is invoked after an error has been thrown by a descendant component. It receives the error that was thrown as a parameter and should return a value to update state.
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return {
error,
};
}
componentDidMount() {
window.onerror = (msg, url, line, column, error) => {
this.logError({
error,
});
};
}
// getDerivedStateFromError() is called during the “render” phase, so side-effects are not permitted. For those use cases, use componentDidCatch() instead.
// This lifecycle is invoked after an error has been thrown by a descendant component. It receives two parameters:
// error - The error that was thrown.
// info - An object with a componentStack key containing
componentDidCatch(error, info) {
// avoid calling log error twice
if (this.lastError && this.lastError.message === this.state.error.message) {
return true;
}
// Example "componentStack":
// in ComponentThatThrows (created by App)
// in ErrorBoundary (created by App)
// in div (created by App)
// in App
// logComponentStackToMyService(info.componentStack);
this.logError({
error,
info,
});
}
async logError({
error,
info
}) {
this.lastError = error;
try {
await fetch('/error', {
method: 'post',
body: JSON.stringify(error),
});
} catch (e) {}
}
render() {
if (this.state.error) {
return display error ;
}
return this.props.children;
}
}
Another way is to store the last error's message in state and check when it happens for the second time.
export default MyComponent extends React.Component{
constructor(props){
super(props);
this.state = {message: null};
}
componentDidMount(){
const root = this;
window.onerror = function(msg, url, line, column, error){
if(root.state.message !== msg){
root.setState({message: msg});
// do rest of the logic
}
}
}
}
But anyways it is good idea to use React Error Boundaries. And you can
implement this global javascript error handling inside the error
boundary component. Where you can both catch js errors (with
window.onerror) and React errors (with componendDidCatch).
My workaround: Apply debouncing (in typescript):
import { Observable } from 'rxjs';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
let errorObserver: any;
window.onerror = (msg, url, lineNo, columnNo, error) => {
if (!errorObserver) {
new Observable(observer => {
errorObserver = observer
}).pipe(debounceTime(300)) // wait 300ms after the last event before emitting last event
.pipe(distinctUntilChanged()) // only emit if value is different from previous value
.subscribe(handleOnError)
}
errorObserver.next(
{
msg,
url,
lineNo,
columnNo,
error
}
)
return true
}
const handleOnError = (value: any) => {
console.log('handleOnError', value)
}
This looks like it's probably firing twice due to the nature of running it on JSFiddle. In a normal build process (with webpack and babel) code with a script error like that should fail to transpile.

Multiple issues in firefox but works properly in chrome with no single issue

I am having multiple issues with firefox but not in chrome.
1) issue in TypeError: response.body is null.
2) while uploading for images i get TypeError: Argument 1 of FormData.constructor does not implement interface HTMLFormElement.
The code where issue is shown are
for number 1 issue (I have used superagent for this)
search( query='Ganesh Chowk' ){
let url = "/api/v1/rental/?place__startswith="+encodeURIComponent(query);
Request.get(url).then((response) => {
if (response) {
this.setState({
place:response.body.objects,
});
} else {
this.setState({
place: [],
});
}
});
for 2nd issue
let image = [];
class RenderPhotos extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
files: []
};
}
onDrop(files) {
console.log('Received files: ', files);
this.setState({
files: files
});
image = new FormData(files);
$.each(files,function(i,file){
image.append('image',file);
});
}
Why this code is working on chrome and not in firefox? What should be changed for this code to work in all browser?
Regarding Error 2: If a parameter is passed to the FormData constructor it must be a HTML <Form> element which does not seem to be the case in your code. So simply drop the param:
image = new FormData();
Update:
Regarding error 1: As you found out yourself the request needs an accept header application/json. Due to differences of Chrome and Firefox default accept headers, the server seems to return a json payload for Chrome and not for Firefox. Anyway the explicit header resolves that issue.

Initialising Okta Signin Widget a second time in a single-page webapp throws exception

We are integrating the Okta Sign-in Widget into our React-based webapp.
The example snippet:
var oktaSignIn = new OktaSignIn({baseUrl: baseUrl});
oktaSignIn.renderEl(...)
Works fine for us when rendering the widget for the first time, but after the user logs in and logs out again, the webapp renders the login component a second time and would attempt to execute the renderEl again to render the widget. This causes the following exception to be thrown:
Backbone.history has already been started
I have created this jsfiddle to demonstrate the problem. It just instantiates a signin widget twice (the second time after a wait). You can see that the second invocation causes the exception to be thrown.
https://jsfiddle.net/nudwcroo/6/
At the moment my workaround is to reload the entire webapp when going to the login component but that is undesirable for a single page app.
Is this a known issue? Is there any way to initialise the sign-in widget twice in a single javascript session?
Since the widget can only be instantiated once per page, it is best to hide/show the element for all Single Page Applications.
<div id="okta-login-container"></div>
<script type="text/javascript">
var oktaSignIn = new OktaSignIn(/* config */);
oktaSignIn.renderEl(
{ el: '#okta-login-container' },
function (res) {
if (res.status === 'SUCCESS') {
// Hide element
$("#okta-login-container").hide();
}
}
);
</script>
When you create your logout() function, make sure to show() the element instead of rendering it again.
function logout() {
$('#okta-login-container').show();
// Do more logic
}
For those experiencing similar problems after following the Okta example provided here: (https://github.com/okta/samples-js-react/blob/master/custom-login/src/Login.jsx)
The problem is with attempting to initialize the widget multiple times within a single page application. I fixed this by only initializing the widget once at the App level, and then rendering it and removing it from the DOM when a child component mounts/unmounts.
Example:
//App.js
class App extends Component {
constructor() {
super()
this.signIn = new OktaSignIn({...})
}
render() {
return <SignInPage widget={this.signIn} />
}
}
--
//SignInPage.js
...
componentDidMount() {
let { redirectUri } = this.state
let { widget } = this.props
widget.renderEl(
{ el: '#sign-in-widget' },
(response) => {
response.session.setCookieAndRedirect(redirectUri)
},
(error) => {
throw error;
},
);
}
componentWillUnmount() {
let { widget } = this.props
widget.remove()
}
render() {
return <div id="sign-in-widget"/></div>
}

Categories