I have an IOT application that communicates with ble devices. So I use react-native-ble-manager.
In this package, you have to use event listeners such as;
bleManagerEmitter.addListener( 'BleManagerDidUpdateValueForCharacteristic', this.handlerCharListenerSettings);
(For more information you can check this)
So I use these event listeners in different screens, but it's not a proper way. Because In every screen that has event listener loads this addListener and this causes problems. In componentWillUnmount event you can clear these listeners, but the correct way is to control all these listeners on one logical place such as App.js or anywhere similar.
My problem begins here:
I have different screens, I want to set their states from App.js. I mean when I change a value from App.js event listener, I want to make the other screens affected by this value. How I can do this or is this a proper way of using listeners?
My RN version is 0.58
If you want to share the listener either use the Context API as mentioned or create a wrapping component. Much like the App.js create a new component:
<Wrapper>
<YourApp/>
<Wrapper/>
class Wrapper extends React.Component {
componentWillMount() {
bleManagerEmitter.addListener( 'BleManagerDidUpdateValueForCharacteristic', this.handlerCharListenerSettings);
}
}
Related
I have a stateless functional component ReceivingComponent that uses a third-party dependency that has a method I want to call when an event from a separate component TransmittingComponent is fired.
In my example I have a Map component that contains a MapBox instance and I'm trying to fire the resize method when it receives an event from a separate component that has a onDrag event.
Most solutions to this problem involve using refs to access a components methods, but as I'm using functional component I can't use refs so I'm exploring other solutions.
I've hacked together something with Redux where I'm setting a boolean value in a store to true, Then inside the ReceivingComponent I have a useEffect that when boolean is true the doThing method is called. The boolean value in the store is then reset to false. It works, but makes my skin crawl.
<ParentComponent>
<ReceivingComponent doThing={}/>
<TransmittingComponent onEvent={} />
</ParentComponent>
In other frameworks I might create an event bus, that emits an event when TransmittingComponent fires its event. And then inside ReceivingComponent I listen for that event to then trigger the method required. But I can't find a method of doing that with React or Redux.
Is there a way with React / Redux to be able to access a components methods from another component without using Refs. Or even emit a global event and listen for that event from another component?
One of the ways to accomplish this in case you have control over the ReceivingComponent is to pass the TransmittingComponent as a child.
<ReceivingComponent>
{({doThing}) => <TransmittingComponent onClick={doThing} />}
</ReceivingComponent>
Here is a sandbox
But if these components are siblings for a reason, another option that comes to my mind is to use some piece of state in the ParentComponent to notify ReceivingComponent that the event was fired.
Here is another sandbox
I suppose it's somewhat similar to what you made with Redux.
It may look not so sexy, but I saw this pattern also in FRP, to convert DOM events somehow into streams.
In React I have built a re-usable component which manages its own state (open/closed) for brevity. Let's call it Panel.
I have another component which makes use of that panel component lets call it Bob.
After calling an API, I would like to trigger Panel (open the panel).
Do I really have to manage state in Bob (the calling component) for whether panel should open and pass it as props? Surely there is a way to manage state ONLY in Panel by exposing a method from Panel.
Here's what I have been doing:
Bob manages state: shouldOpen: boolean;
When Bob needs the panel to open: this.setState({shouldOpen: true});
<Panel
shouldOpen={this.state.shouldOpen}
/>
Panel has a separate state {isOpen: false}
What I would like to do, is expose a method in Panel which can be called from Bob. This would eliminate the need to manage state in both Bob AND in Panel. So that when I call an API, I can call openPanel
public openPanel = () => {
this.setState({isOpen: true});
}
public closePanel = () => {
this.setState({isOpen: false});
}
I'm looking for the RIGHT way to do this. If that means I need to structure my code differently, please provide guidance. To me it just makes sense that panel should manage its own open/closed state.
In React you often find the pattern, that a reusable component offers an interface via props, e.g in your case an isOpen in Panel. However, your Panel component does not have to store anything in its state and should rather use this.props, if it is a component that is 'controlled' by its parent like in your case - this is completely fine and good code so don't worry about it.
To answer the other part of your question: yes it is possible to call methods on your Panel component by using React.createRef, please see How to access component methods from “outside” in ReactJS?.
I am current working with some complex stateful React components I installed through npm. For example, a react-bootstrap-table component. It accept data through its props. But it keeps a lots of states of its own, for example, which row is currently selected. I am adding a button, on clicking I would like to clear all the selected row information. Not having access to any API that can do that, I am wondering if I can totally reset the table component.
Right now I use a wrapper component that render the table based on a flag. It looks like:
class wrapper extends React.component{
render(){
if(this.props.flag==true) return <React-Table />;
else return null;
}
}
Now by toggling the flag, I can force a re-render of the table component with its original state.
I am wondering if there is a more straightforward way to do this?
After searching the react-boostrap-table API, it found a method reset (see docs):
Call reset to clean all the status on the table currently.
this.refs.table.reset(); // this.refs.table is a ref for BootstrapTable
To give a more general answer,
React does not have a general way to reset a component externally, the component should provide it if it's necessary. There are 2 ways for parent component to communicate with child component, through props, or not frequently, using ref to directly call a child method. Resetting is using the second way.
For react-bootstrap-table, there are reset() and cleanSelected() method you can call. How to call child component method.
In a nutshell
After using React for a few months - the idea of using this to keep track of changing variables in a component has come to feel like snorkeling in the North Pole - no one should do it,ever.
But with Leaflet that is kind of what happens(and for details I'll skip, I can't use the really sweet leaflet component wrapper that now exist.
The problem that lead me to this:
I'm trying to save the initial zoom level into a store as state, but since I'm using an Action that changes the rendering path the opens the Map Component I can't call another Action as the MapComponent mounts without getting a chain Action error "Invariant Dispatch". I also couldn't find any async updates to zoom in the Leaflet Docs to get around the synchronous Actions error.
Without the initial zoom I can't see if the first zoom the user makes is up or down :(
My Hack Solution:
Since the rest of the map is saved in this I just created another property of this called this.currenZoom that gets initialized as the component mounts and updated when zoomStartis called.(technically updates like state)
My Question:
Am I snorkeling in the North Pole using this to keep my zoom state? Or is that acceptable since Leaflet technically isn't working with the virtual DOM the same way? Is using this okay to manage variable updates in some cases in our components.
Note: This question might come of as peevish, but seriously I've went so long using state and props for everything that it just feels MEGA hacky using this in my components.
I do think it's fine to keep data directly on this if and only if it doesn't affect rendering (although I wouldn't do so unless there's a good reason not to simply put it in the component's state). The render method should always be a function of this.state and this.props (and only those two things).
Doing this is most often used as an "escape hatch" mechanism--like the scenario you mention here--where some library, plugin, or function doesn't interact with the virtual DOM in the same way as a normal component.
You can even see the React documentation using this method in the SetIntervalMixin mixin example:
var SetIntervalMixin = {
componentWillMount: function() {
this.intervals = [];
},
setInterval: function() {
this.intervals.push(setInterval.apply(null, arguments));
},
componentWillUnmount: function() {
this.intervals.forEach(clearInterval);
}
};
I'm trying to build a reusable component that I can use to load ads via Doubleclick (DFP) asynchronously. The problem I'm running into is there are several dependencies before an ad can actually load, and I'm not sure the best way to handle the communication and sequence. Because the ad components render anywhere on the page and don't render as part of a parent, I'm not sure how to handle it since I can't embed ads inside of a parent to communicate.
In this article:
http://facebook.github.io/react/blog/2013/11/05/thinking-in-react.html#step-4-identify-where-your-state-should-live
There is this potential solution, but after some searching I wasn't able to find an example that doesn't tie the rendering together.
"If you can't find a component where it makes sense to own the state, create a new component simply for holding the state and add it somewhere in the hierarchy above the common owner component."
Here are the details/steps:
1). I first need to load the JS http://www.googletagservices.com/tag/js/gpt.js
This creates a global googletag object.
2). I need to set global targeting (i.e. site name, content type, etc) that will apply to all ad tags (configurable).
Example
googletag.pubads().setTargeting('site', window.location.hostname);
3). I need to define the ad slots and targeting for individual ad slots that React rendered.
googletag.defineSlot('/12345/zone', [[300, 250], [300, 600]], 'div-gpt-ad-300x250-0').addService(googletag.pubads()).setTargeting('pos', 'sidebar_300_1');
4). I need to specify settings globally
googletag.pubads().collapseEmptyDivs();
googletag.pubads().enableSingleRequest();
googletag.enableServices();
5). I need to render the ad slots (with the option to render individually or globally)
googletag.cmd.push(function() {
googletag.display(divIdAttribute);
});
or just
googletag.display();
I Initially created two components: a DFPManager that loads the required Javascript once, and then a DFPAd that actually handles the individual ads. I couldn't figure out the communication.
I also need to be able to reload individual ads based on external events (i.e. ajax page change) or create new ads as a user scrolls into infinite scroll content.
Is the best solution to manage my own events system where I put listeners in my components to depend on external events, or is there a better way to manage this? In that case I wouldn't necessarily need a DFPManager since no rendering takes place there and I could trigger an event when ready. This is a similar problem for analytics tracking where I need to track pageviews based on external events, but only after first loading an initial javascript (i.e. Google Analytics tracking js).
<div id="300-250-ad"></div>
<script type="text/jsx">
/** #jsx React.DOM */
React.renderComponent(
<DFPAd size={[[300, 250], [300, 600]]} targeting={[["pos", "sidebar_300_1"]]} />,
document.getElementById('300-250-ad')
);
</script>
I may be overthinking this. Appreciate any suggestions.
Turning DFPManager into just an object which fires change events sounds like a good plan. It wouldn't be a React component itself any more. The individual ad components could accept the store object as a prop, then subscribe to the change events in componentDidMount, and unsubscribe in componentWillUnmount. The onChange handler could call setState with the current data from the store, which will trigger a re-render.
The store could be implemented like the store in the Flux TodoMVC example, which uses an EventEmitter base class. If there is no interactivity, using a full dispatcher is probably overkill, since there won't be any user actions by the sounds of it.