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.
Related
I have a query with redux + react native ..
I have a redux state that contains data, that data is constantly changing.
I also have several components that need to read this data.
The question is that all the components are on the screen, and when one makes a modification to the redux state, this must be reflected in the others too, I can not find any solution, the best thing would be something that makes the components render again that they are listening to the state but I can't do it .. I need help!
Try to use event emitter for call methods to get data from methods in all components.
Ref link:- https://medium.com/#filipedegrazia/using-custom-events-in-react-native-831a809f279d
Event emitter emits an event from one component to another to call any function or pass data also. Call get method in all components to emit method after data changed.
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);
}
}
I'm building an application where I would like to provide separate views for same data.
In my current implementation, data is obtained by web service call and persisted in state of App component in App.js. App component hosts (renders) another component called StackEditor, which acts as a view for this.state.components in App component.
UI elements rendered by StackEditor can be moved around, and to synchronize state of App I do it as below:
<StackEditor
components={this.state.components}
onLocationChanged={this.handleLocationChanged} />
In handleLocationChanged I update the state:
handleLocationChanged(e, data) {
this.setState(prevState => {
// event data copied to state here
return {components: prevState.components};
});
}
As state is now updated, this forces StackEditor to be rendered again, as its property components is bound to state as components={this.state.components} (see in the code snippet above).
This all works, but now I started questioning if I'm doing it right.
Q1: Should I be using state instead of props?
It seems that location of component is mutated in principle, although from StackEditor point of view, it is immutable (I can decide that change is invalid and not to update the state in event listener).
Q2: Is it possible to share part of the state between 2 components in React?
If I somehow convert StackEditor from getting components from state instead of props, will I get notification on state changed by child component (StackEditor) in my parent component (App)?
Q3: Also, are props more convenient to use than state in general?
When I created another component following HOC guidelines (https://reactjs.org/docs/higher-order-components.html) I discovered that props are easily forwarded to "wrapped" component, but not state. If I provide a function to call back via property (as I did above), "wrapped" component can easily call it, without even noticing that it's "wrapped". I don't see how I can easily notify "wrapped" component about state change in "wrapper", without writing some extra code.
If you imagine your application to be a tree of components in a well designed app it's usually like this:
the leafs are stateless components . They decide how data is rendered.
the nodes are stateful components. They decide which components and data to render.
Q1: Should I be using state instead of props?
It depends on which category of components you have (node or leaf).
Q2: Is it possible to share part of the state between 2 components in
React?
If you feel that your app has a lot of state that mutates and needs to be used by several components spread over your tree you usually start to introduce an external state management library (e.g. redux). Components can subscribe to your store and become stateless as your store now handles the state.
Q3: Also, are props more convenient to use than state in general?
They solve different problems so you can't really say that. A stateless component is usually easier to understand but has no capabilities to control anything.
Also read Identify where your state should live and When to use redux.
All that is only a rule of thumb. A lot of the time you will have components that have both state and props because they control parts of your app but delegate other parts to their children.
This all works, but now I started questioning if I'm doing it right.
As far as I can see from the code you provided this looks pretty much as it has to.
I don't feel right to store a function in a reactjs state, but not sure any disadvantage? Could anyone help analyze it?
view1: function() {
return <div>view1</view>
}
view2: fucntion() {
return <div>view2</view>
}
goView1: function() {
this.setState({contentView: view1})
}
goView2: function() {
this.setState({contentView: view2})
}
render: function () {
return(
{this.state.contentView()}
)
}
There're a few common gotchas about state in react:
Don't mutate state directly, use setState instead
Don't use state when not referenced in render, use instance variable instead.
Well, it seems fine to use state as function, since it's used in your render function to return what to render? Alright, it might not be an anti-pattern, but a better place to put those functions might be in your props, aka render prop, a few reasons:
Think state as your component's data model.
Data are not stale, it changes over time, it could be results from async operations, ui states after user interactions, controlled forms values etc.
Just like redux stores your app's global state, react state should store your component's local state that changes over time, while functions most of time, does nothing more than deferring execution.
Render props is different, it offers IoC (Inversion of Control) for free.
Props most of time similar to local state, should be concerned with UI rendering. However, unlike state owned/managed by component itself, props are passed from parents/clients of the component.
Event handlers for example, event target (usually DOM elements like button, input etc) doesn't need to know how to handle events when creating the component, instead it yields control back to component client/consumer who might need to use setState in its handleEventName handler to update certain container's state shared across children.
Some library (e.g., react router4) also shifted from HOC to render props (e.g., react 16.3 new context API - provider/consumer pair) in favor of its flexibility and explicitness. Instead of composing HOCs before runtime for static version of final enhanced component, you can declare what to render using render props and composing them at runtime in render functions. Thus, you get a clear separation between data (state/props) provider and consumer (where render props are passed from).
Regarding any performance (re-render) concerns, i think it's totally fine to use state as function, since most performance tricks are done by using shouldComponentUpdate hook with shallow compare state/props references (e.g. React.PureComponent, Redux Connect etc), so it's where immutability and memoization comes into play.
From the changelog for React v0.13:
"ref resolution order has changed slightly such that a ref to a component is available immediately after its componentDidMount method is called; this change should be observable only if your component calls a parent component's callback within your componentDidMount, which is an anti-pattern and should be avoided regardless"
So, is there a better way to access this.refs after the DOM mounts? I dislike using setTimeout(), but that is the best method I see to work on a specific DOM after it mounts. Is there a replacement method I am missing? Or, is the best way to attach an onLoad method directly to the component?
Maybe you are reading too much into this.
My interpretation is that all refs to child components are available in componentDidMount.
The change is that they are not available before the child component's own componentDidMount has completed (which happens before the parent component's). And you would only be affected by that if you call some callback from inside componentDidMount of the child, and that code tries to use the ref immediately. Which seems like an edge-case and, as they say, an anti-pattern.
So "normal usage" should not be impacted by this change.
Please try and report back.