react-native-audio-toolkit, isPlaying is not working - react-native

import React from "react";
import { Player } from "#react-native-community/audio-toolkit";
export default class RNAudiotoolkit extends React.Component {
componentDidMount(){
new Player("some_audio_file.mp3").play();
console.log(Player.isPlaying);
}
}
Above is the minimum code I've whittled down to, the audio track does play but, console.log(Player.isPlaying) always returns "false" but the audio file is running. Any input on why this isn't working. I can only suspect it has something to do with MediaStates, but have unsuccessfully gotten anything to work. If you have experience with this package your input is greatly appreciated.
documentation: https://github.com/react-native-community/react-native-audio-toolkit/blob/master/docs/API.md

Edit: Fixed and tested; answer is a combination of my original and mAhMoUdDaFeR's answer.
If you look above the documentation for the play method, you will see the documentation for prepare(Function callback). In it, it states:
...Otherwise the file is prepared when calling play() which may result in a small delay.
This means that if you check the .isPlaying property immediately after calling play() like you are doing, it is not guaranteed that the file is actually playing by time your console.log(Player.isPlaying) executes.
There is also the second issue that .isPlaying is not a static property on the Player class despite how it appears in the docs. It is actually a property of the Player instance that you need to create to play an audio file.
If you want to see that .isPlaying is indeed working correctly, one potential check is to run your code in a callback function passed into .play() as the docs show:
play(Function ?callback)
Start playback.
If callback is given, it is called when playback has started.
So a simple example would be to write your example code like this (saving the created instance and then logging in a callback):
componentDidMount(){
const p = new Player("some_audio_file.mp3").play(() => console.log('in callback', p.isPlaying));
console.log('immediately after play', p.isPlaying);
}
I created a new project to test this and if you run the above code, you'll see the following printed out illustrating the issue:
immediately after play false
in callback true

isPlaying is not a static method in the component Player, so you can't use Player.isPlaying, you can get isPlaying from the created instance (object) of this Player.
Try keeping a reference of the player object and then accessing its children:
this.player = new Player("some_audio_file.mp3").play()
and then log:
console.log(this.player.isPlaying)

Related

vue-router : why watch on $route trigger when previous queries rewrite with same values

i have a button in my project when i click on it two queries added to URL
onClickBtn(){
this.$router.push({queries: {name:'kevin' , age:21} })
}
and I have a watch on $route
watch:{
$route:function(){
// call API
}
}
and when i click on that button severall time
watch calls my API every time although nothing has changed
and this make a problem form me
because nothing has changed in route But API is called and the same data is
received .
what should I do to avoid calling API in watch , when queries don't changed ??
The object you are pushing on the router is always different, that's why the $route watch is launched.
You can compare the data you receive in the watch, so that when they are different then you invoke the API:
watch:{
'$route' (newRoute, lastRoute){
// Check if query is different
// call API
}
}
On top of the answer that Cristian provided, you could also even double-check if your stuff has changed before even pushing a new object to your router.
Like this
checkIfUpdateNeeded && this.$router.push({queries: {name: 'kevin', age:21 } })
That way, you will have less moving parts and you won't have a trigger in the watcher for "nothing", especially if you're pushing a bigger object and want to make a deep-diff between 2 objects.

How to call method whenever screen loaded, Like always should call method in react native

I am developing react native project and I am loading some graphs from server response.
It is a Tab based app and this code is written in first tab.
But, In some use cases that data is not loading to that graph properly.
I have written that code in componentDidMount(), But it will call only once. But, My requirement is I have to call whenever view loaded, That time only render method is calling.
I have tried to add addlistener for navigation, But, Due to its it not navigation stack throwing error.
I have found some solution like below.
componentDidMount() {
}
fetchGraphData = () => {
//some code fetching from DB and redux based on conditions
}
render() {
this.fetchGraphData();
return (
);
}
}
But, This is not good practice as per code standards.
I am not receiving props, But, We are using some graphs which are
loading from data. My requirement is I have to call api fetch data
method after screen load every time.
Any suggestions, I have to call that fetchGraphData() once render method or view loaded.
Your problem is that when you move the 'fetchGraphData' function to a screen with the 'fetchGraphData' function, you must execute it. This problem can be solved by something simpler than I thought.
componentDidMount() {
this.fetchGraphData();
}
You can try rendering again when you move to a screen with a function.
this.props.navigation.push('functionMoveScreen') // Rendering the screen again.

Vuetify and require.js: How do I show a dynamic component?

I am creating a tab component that loads its v-tab-item components dynamically, given an array of configuration objects that consist of tabName, id, and tabContent which is a resource location for the component. I have it successfully loading the components. However, they don't actually initialize (or run their created() methods) until I switch tabs. I just get empty tabs with the correct labels. Using the DOM inspector initially shows just <componentId></componentId>, and then when I switch tabs, those tags are replaced with all of the component's content.
How do I get the dynamic components to initialize as soon as they are loaded?
EDIT: I created a CodePen here:
https://codepen.io/sgarfio/project/editor/DKgQON
But as this is my first CodePen, I haven't yet figured out how to reference other files in the project (i.e. what to set tabContent to so that require.js can load them up). I'm seeing "Access is denied" in the console, which makes it sound like it found the files but isn't allowed to access them, which is weird because all the files belong to the same project. So my CodePen doesn't even work as well as my actual project. But maybe it will help someone understand what I'm trying to do.
Also, after poking around a bit more, I found this:
http://michaelnthiessen.com/force-re-render/
that says I should change the key on the component and that will force the component to re-render. I also found this:
https://v2.vuejs.org/v2/guide/components-dynamic-async.html
Which has a pretty good example of what I'm trying to do, but it doesn't force the async component to initialize in the first place. That's what I need the async components to do - they don't initialize until I switch tabs. In fact they don't even show up in the network calls. Vue is simply generating a placeholder for them.
I got it working! What I ended up doing was to emit an event from the code that loads the async components to indicate that that component was loaded. The listener for that event keeps a count of how many components have been loaded (it already knows how many there should be), and as soon as it receives the right number of these events, it changes the value of this.active (v-model value for the v-tabs component, which indicates which tab is currently active) to "0". I tried this because as I noted before, the async components were loading/rendering whenever I switched tabs. I also have prev/next buttons to set this.active, and today I noticed that if I used the "next" button instead of clicking on a tab, it would load the async components but not advance the tab. I had already figured out how to emit an event from the loading code, so all I had to do at that point was capture the number of loaded components and then manipulate this.active.
I might try to update my CodePen to reflect this, and if I do I'll come back and comment accordingly. For now, here's a sample of what I ended up with. I'm still adding things to make it more robust (e.g. in case the configuration object contains a non-existent component URL), but this is the basic gist of it.
created: function() {
this.$on("componentLoaded", () => {
this.numTabsInitialized++;
if(this.numTabsInitialized == this.numTabs) {
// All tabs loaded; update active to force them to load
this.active = "0";
}
})
},
methods: {
loadComponent: function(config) {
var id = config.id;
var compPath = config.tabContent;
var self = this;
require([compPath], function(comp) {
Vue.component(id, comp);
self.$emit("componentLoaded");
});
}
}

Reflux setState with a Callback

EDIT AGAIN: Opened an issue with Reflux here: https://github.com/reflux/refluxjs/issues/544
EDIT: Reflux setState does not provide any callback for setState. They require you to use the component lifecycle methods to ensure the state is set prior to running any code. If you ever need to use the reflux setState outside of a component, where you do not have lifecycle methods, you will not be guaranteed the state is set. This is due to how Reflux does their setState. It loops all listening components and calls those components' setState methods. If Reflux were refactored to wait until all the listening components' setState calls complete then call a callback passed into its own setState method, that may work, but it would likely require a large rework of Reflux. I have started using a singleton class to manage some of these variables, as they are fully outside the component lifecycle.
Can you use setState with a callback in ReactNative or is that only in React? I'm using the below syntax and the first debugger is hit, but the second debugger and console log never get hit.
EDIT: After digging some more, it seems this does not occur when using setting the state directly, but only when running it through a reflux store and/or not using a component.
See snack here: https://snack.expo.io/S1dm3eFoM
debugger
this.setState(
params,
() => {
debugger
console.log("CALLIN IT BACK")
}
)
I'm the creator of Reflux's ES6 styled stores/component hookups. Hopefully I can shed some light on this for you.
Here's the important points:
1) Reflux sets its store state immediately upon setState calls.
Reflux's store state doesn't have the same problems as React and doesn't need React's workaround (callback). You are guaranteed that your change is immediately reflected in the store's state, that's why there is not a callback. The very next line of code will reflect the store's new state.
tl;dr, no workaround is required.
// in Reflux stores this works
this.setState({foo:'foo'});
console.log(this.state.foo === 'foo') // true
this.setState({foo:'bar'});
console.log(this.state.foo === 'bar') // true
2) Stores can never depend upon components!
The idea that the setState would give a callback about when the dependent components have all updated their state is a major violation of the single most fundamental of all flux principles: 1 way data flow.
If your store requires knowledge about whether or not components are doing something then you are already doing it wrong, and all the problems you are experiencing are XY problems of fundamentally not following flux in the first place. 1-way data flow is a main flux principle.
And that principle exists for good reason. Flux doesn't require 1:1 mapping of store state properties to component state properties. You can map anything to anything, or even just use the store's state for the building blocks of how you will run your own logic to create completely new state properties on the components. For example having loaded and transitioned as separate properties in store state, but mapping to a loadedAndTransitioned property in one component, and a notLoadedOrTransitioned in another component via your own custom logic. That's a hugely powerful part of flux. But your suggestion would pretty much destroy all that, since Reflux can't map people's custom logic.
1-way data flow must be maintained; Store's must operate the same independently of what components utilize them. Without this, the power of flux falls apart!
Store's listen to actions, components listen to stores, actions are called from wherever. All flux-based data flows from action -> store -> component only.
I've checked the library for the refluxjs and the problem and the workaround are as mentioned below.
Problem
The library provides with a new instance of the setState which is not exactly similar to ReactJS setState, which omits the callback as mentioned in their code below.
/dist/reflux.js
proto.setState = function (obj) {
// Object.assign(this.state, obj); // later turn this to Object.assign and remove loop once support is good enough
for (var key in obj) {
this.state[key] = obj[key];
}
// if there's an id (i.e. it's being tracked by the global state) then make sure to update the global state
if (this.id) {
Reflux.GlobalState[this.id] = this.state;
}
// trigger, because any component it's attached to is listening and will merge the store state into its own on a store trigger
this.trigger(obj);
};
Also as mentioned here in the docs
That store will store its state on a this.state property, and mutate its state via this.setState() in a way that is extremely similar to React classes themselves.
WorkAround
The library provides with the listener functions, which provide us with the callbacks of the setState obj of the ReactJS as mentioned in the below snippet.
/dist/reflux.js
componentDidMount: function() {
var me = this;
_.extend(me, ListenerMethods);
this.listenTo(listenable, function(v) {
me.setState(_.object([key],[v]));
});
},
You can use them in the following way
this.listenTo(action, callback)
Hope it clears the doubts
Edit:
Usage as per the docs
To listen inside of the store
constructor()
{
super();
this.state = {count: 0};
this.listenTo(increment, this.incrementItUp);
}
incrementItUp()
{
var newCount = this.state.count + 1;
this.setState({count: newCount});
}
To listen outside of the store anywhere
// listen directly to an action
myActions.actionName.listen(myCallbackFunc);
// listen to a child action
myActions.load.completed.listen(myCallbackFunc);
Here's the link to the snack with working callbacks based on Promises

React Native Modules Not Working Together

I have a react native application I am building, and for part of it I have implemented my own custom module (in this case a webview). I have it set up and everything works fine, until I render two of the same modules on the same screen.
Once I have had two of the same modules on the same screen, the code in Objective-C which calls the onChange method back to javascript no longer executes and I can longer longer communicate with my module through Javascript.
I've noticed that each instance of my module has a tag or something like that, but I am not sure what I should do to fix this.
Update
Basically I have added some code and I can see that the issue is when the new module is added to the screen, it overrides the callbacks for the previous module. Once the new modules leaves the screen, the callbacks in Objective-C are still "focused" on the most recently rendered module...
Might this have something to do with?
ReactNative.findNodeHandle(this.refs[WEB_VIEW]);
Edit
I have been looking over some open source code and I think I have narrowed down the problem. Basically my module consists of a WebView and a WebViewManager. The WebViews are pretty simple and just display the contents of the web and such, and the manager is responsible for controlling their behavior.
When I want to call a method on the module from React Native, I can do so by calling the method on the WebViewManager like this:
scrollToTop() {
RNTWebViewManager.scrollToTop();
}
In which the manager will then call the corresponding method in Objective-C. However, it seems that when I have more than one instance of a WebView on screen at a time, the manager doesn't know which instance to call, and resorts to calling the most recent instance that was placed on screen.
In the open source project I see that they do something by passings refs around and getting the specific view tags, which is then used by the manager to call on the correct instance of their WebViews, but I am still trying to figure out how to implement this.
Here is the link below for the project I was looking at:
https://github.com/CRAlpha/react-native-wkwebview
Ok so after quite some time and research I have finally figured out the solution. Basically it has to do with making sure the right component is being called from React Native and Objective-C. For example in your react native component you have a function:
executeJavascript(tag,js) {
RNTWebViewManager.executeJavascript(tag,js);
}
And then in your Objective-C implementation of RNTWebViewManager you could have something like this:
RCT_EXPORT_METHOD(executeJavascript:(nonnull NSNumber *)reactTag with:(NSString *)js)
{
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RNWebView *> *viewRegistry) {
RNWebView *view = viewRegistry[reactTag];
[view evaluateJavaScript:js completionHandler:^(id _Nullable result, NSError * _Nullable error) {
if (result == nil) { result = #""; }
id outputError = (error) ? error.description : #"";
view.onChange(#{ #"result" : result, #"error": outputError, #"type" : #"javascript" });
}];
}];
}
Notice how the executeJavascript function takes a tag, that is a reference to the current view you are trying to invoke that method on. So basically all you need to do is pass that tag number whenever you want to perform a method on a specific instance, which you can get like so:
getTag() {
return ReactNative.findNodeHandle(this.refs['RNWebView']);
}
Where in this case the ref is 'RNWebView', which would give me back the right tag which I could then use to call the function. Hope I did a decent job of explaining this.