React Native Event Emitter between components - events

I have two components in react native. One class name is firstClass, and the second class is secondClass. "secondClass" is a child view of firstClass. I can submit my data to my firebase from the child view, but on successful submit, I want the view to go back to "firstClass".
I have used event emitters in the past, but those were only using navigatorIOS.
My question is, does the event emitter system listen to global events, or just within their class? And if so, how do I communicate from "secondClass" to "firstClass", that I want to show "firstClass" again.
Thanks!

You can use a callback function and call it from the second component :
in the first component:
//Constructor
....
this.state ={
renderSecondComponent:false
}
....
...Your code
{
this.renderSecondComponent()
}
renderSecondComponent(){
if(this.state.renderSecondComponent){
return(
<SecondComponent callback={()=>{this.setState({renderSecondComponent:false})}}/>
)
}
}
In the second component
....Your code
// when you should go back to the first component
if(this.props.callback){
this.props.callback()
}

Related

Laravel 9: Problem emitting event from child component to parent

In my Laravel 9 project, I have a parent component, whose template includes a child component.
template>
....
....
<div v-if=item.is_visible class="col-4">
<note-details-modal-component v-bind:is_visible=item.is_visible :note_id= item.id>
</note-details-modal-component>
</div>
On clicking a button in the parent component, I set is_visible to true that renders the child component through v-if. In the child component, when I press a button, it calls a method that emits an event to the parent.
closeNow: function() {
console.log('closeNow');
// this.$parent.$emit('close_note',false);
this.$emit('close_note',false);
} ,
In the parent component, I have a method.
close_note(value)
{
console.log('In parent');
this.is_visible = ! this.is_visible;
},
When I click the close button in the child component, it calls CloseNow() in the child component, and I see that in the console.log. However, that event does not emit to the parent. I have tried all suggestions that I could find on the web. Also, I do not see any errors in the Dev console.
Could someone please tell me what's wrong in my code that is preventing the event from emitting from the child component to parent?
Thanks in advance.
The thing is that nothing refer to the emit you made. If you have this:
closeNow: function() {
console.log('closeNow');
this.$emit('close_note',false);
}
You should mention the close_note when you call the child component. It should be something like that:
<note-details-modal-component v-bind:is_visible=item.is_visible :note_id= item.id #theEmitName="theFunctionYoucall">
</note-details-modal-component>
where theEmitName is close_note and the function you call has the same name. This medium can be usefull : https://medium.com/js-dojo/component-communication-in-vue-js-ca8b591d7efa

react-redux together with components status

in a react UI I have a table component. You can edit one row of the table by clicking a edit button or you can add a new record by clicking a "new-record-button". When clicking the edit button an redux-action is triggered which takes the row and sets a visible property of a modal dialog. When the "new-record-button" is clicked an action is triggered which creates a new empty data item and the same modal dialog is triggered.
In the modal dialog I have several text components with onChange method.
in this onChange-method the data-item is written.
When to user clicks a save-button the edited dataItem is saved to the database.
So my code looks like:
const mapStateToProps = (state) => ({
dataItem: state.datItemToEdit || {},
...
});
...
handleTextChange(event) {
const {
dataItem
} = this.props;
const id = event.target.id;
const text = event.target.value;
switch (id) {
case 'carId': {
dataItem.carId = text;
break;
}
...
}
this.forceUpdate();
}
...
<TextField
...
onChange={event => this.handleTextChange(event)}
/>
I have several question regarding this approach. First I do not understand why in handleTextChange we can write to dataItem. It does work apparently.
dataItem.carId is set in the example code but I thought
const {dataItem} = this.props;
gives us a local read-only variable dataItem just to read from the props...
Next thing I think is a poor design. After reading in a book about react I think we should not write to props but only set a state.
In my example I get the the dataItem from the redux-state. The mapStateToProps maps it to the (read-only) props of the component, right?!. But I want to EDIT it. So I would have to copy it to the state of my component?
But where to do it?
Once in the state of my component I could simply call this.setState for the various text-fields and the component would render and I could abstain from forceUpdate(), right?!
Can someone explain how the redux status plays together with the component status and props for this example?
In redux or react, you shouldn't write to the props directly because you should keep your props as immutable. Redux forces us to use immutable state because state is a source of truth for the application. If the reference to state changes then only your app should render. If you'll mutate your state (objects) then the references don't get changed and your app doesn't know whether some state has been changed or not. React/Redux doesn't give you read-only objects automatically. You can mutate them anytime but as I told you, it can cause problems that Your app won't know when to re-render. If you want to have this read-only property inherently, you should probably use immutable.js
About your second question that you'll have to copy the props to the component's state and where you should do it. You should do it in the constructor of the component and you should use immutibility helper
import React from React;
import update from 'immutibility-helper';
class Modal extends React.Component {
constructor(props){
this.state = {
dataItem: dataItem,
};
}
...other methods
handleTextChange(event) {
const {
dataItem
} = this.props;
const id = event.target.id;
const text = event.target.value;
switch (id) {
case 'carId': {
this.props.updateItem(this.state.dataItem, text); //fire a redux action to update state in redux
this.setState(update(this.state, {
dataItem: {
carId: {$set: text},
}
});
break;
}
...
}
}
}
You wouldn't have to do forceUpdate in such case because the reference to state will change and the component will re-render itself.
Also, you can use forceUpdate in your application but personally I don't find it a great idea because when React/Redux is giving you the flow of state, by using forceUpdate, you're breaking the flow.
The last question is how redux and react state plays together. That is also a matter of choice. If I have a app level state, e.g., in your case you've some app level data, you should put that in your redux state and if you have a component level things, such as opening a modal or opening a third pane. That's the convention I follow but that can really depend on how you want to exploit react and redux state.
Also, in above code, I put the redux state in component state too (because you asked where to put that) but Ideally you should fire a redux action and update in redux state. In this way, you will restrict yourself from state duplication in react and redux.
import React from React;
import {updateItem} from './actions';
class Modal extends React.Component {
...other methods
handleTextChange(event) {
const {
dataItem
} = this.props;
const id = event.target.id;
const text = event.target.value;
switch (id) {
case 'carId': {
this.props.updateItem(this.props.dataItem, text); //fire a redux action to update state in redux
break;
}
...
}
}
}
const mapStateToProps = (state) => ({
dataItem: getDataItem(state), //get Data Item gets Data from redux state
});
export default connect(mapStateToProps, {updateItem: updateItem})(Modal);
in Actions:
updateItem = (dataItem, text) => dispatch => {
dispatch({type: 'UPDATE_ITEM', payLoad: {dataItem, text});
};
in Reducer:
export default (state = {}, action) => {
switch(action){
case 'UPDATE_ITEM': {
return {
...state,
dataItem: {
...action.dataItem,
carId: action.text,
}
};
}
}
}
In this way, your state will be pure and you don't have to worry about immutibility.
EDIT:
As constructor will be called only once, you should probably use componentWillReceiveProps so that whenever you render the component, you get the next updated props of the component. You can check whether the carId of dataItem is same or not and then update the state.
componentWillReceiveProps(nextProps){
if(nextProps.dataItem.carId !== this.props.dataItem.carId){
this.setState({dataItem: nextProps.dataItem});
}
}
You should only use redux when you want different, unrelated components in your app to know and share the specific state.
e.g. - When a user logs in to your app, you might want all components to know that user so you'll connect your different containers to the user reducer and then propagate the user to the components.
Sounds like in this case you have a classic use case for using the inner state.
You can use the parent of all TextFields to maintain all rows, edit them by index, etc.
Once you start using redux, it's really easy to make the mistake of transferring the entire state of the components to the reducers, I've been there and stopped doing it a while ago :)

[admin-on-rest]: Search only when search button is pressed

this is related to admin-on-rest reactJs based framework.
I need some help/pointers on enabling search only when a search button is pressed. I need to collect multiple inputs from the user and finally push a query via REST Api to the back-end when a search button is clicked.
The query searches a very large DB set and I don't want to push queries to back-end as user is typing.
Here is what you need to do.
You need to write a custom Redux Form component. Something like below. I haven't tested this code. But I hope you get the general idea. You will also need to handle all the props that are passed down by Redux Form to your component. Take a look at the Redux Form docs. Also take a look at the TextInput component of Admin-On-Rest in the source code.
const ENTER_KEY = 13;
class RenderTextField extends Component {
handleKeyDown = (event) => {
switch( event.keyCode ) {
case ENTER_KEY:
this.props.input.onChange(this.state.searchQuery);
break;
default:
this.setState({this.searchQuery: event.target.value})
}
}
render() {
return (<input onKeyDown={this.handleKeyDown}
//You will need to look at the ReduxForm docs and handle all the props that are passed to every ReduxForm component.
/>)
}
}
4) You can also use material UI components with ReduxForm.
http://redux-form.com/6.0.0-rc.1/examples/material-ui/

Redux Saga Behavior of Component Life-cycle

This is a general question. I have a redux saga yielding calls that update the store every x mins and show the store gets updated appropriately in the redux dev tool. In the render method of my component if I click before the data I will get a spinner and if I click after the component will render; HOWEVER, in the components class the life cycle "componentWillUpdate" or "componentWillReceiveProps" shows the connected piece in redux store as undefined in either method yet the render is able to pass the correct props; what the cluck? I'll head back to the docs but this seems odd.
...
//dont usually use this for redux
componentWillReceiveProps(){
console.log(dailyOperations) // nothing here
}
componentWillUpdate(){
console.log(dailyOperations) // nothing here
}
render(){
if (dailyOperations === undefined) {
return (<SpinnerThing />)
else
return (<SomeDisplayComponent data={dailyOperations} />) //Data is here
}
I couldn't at the time account for it but now it makes sense and if it helps anyone else great. It will not be there on the update for the connected redux state but through "componentWillUpdate(nextProps)" & nextProps will have it.

How to Include events (not event-plugin) in Ractive initialization/defaults?

I've read through the Ractive Documentation and I'm scratching my head a bit, because it seems like the default events initialization option allows me to do something - create new eventtypes - far more complex than what i need but conversely, there's no hook for the simpler, (more common?) task of defining default events
Could someone advise on how to provide global events that could be fired for traditional DOM events?
Example:
I have a 3 Component application page. I want to define a getOptions event, such that any <select on-click='getOptions'>...</select> will be handled by the same function. I don't want to have to define that function in each component.
My intuition would have been to do the following:
Ractive.events['getOptions'] = function(event){
//logic for getting the options for the value in event.keypath
}
or, if i wanted a true default that could be overridden...
Ractive.default.events['getOptions'] = function(event){
//logic for getting the options for the value in event.keypath
}
but my understanding of the documentation, is that Ractive.events and Ractive.default.events do not provide this, but rather provide a way to define new event plugins, that depend on a separate mechanism for getting fired:
Ractive.events.getoptions = function(node,fire){
//here goes logic for interacting with DOM event listeners, etc
}
//and then i would need to do this
ractive = Ractive.extend({...});
ractive.on('someOtherEventName',function(event){
//logic for getting the options for the value in event.keypath
});
//and then I could do this...
<select on-getoptions='someOtherEventName'>...</select>
but what would fire the getoptions in this case - from the template, rather than js ractive.fire()?
Would something like <select on-getoptions='someOtherFunction' on-click=getoptions>...</select> work? That seems very strange to me. Do I understand the concept correction? If not, what am i missing?
Is there a simple way to achieve the first example?
Ractive.events refers to custom events for mediating between the dom and the template:
Ractive.events.banana = function( node, fire ) { ... };
<div on-banana="doSomething()"/>
The handler for the event can either be the name of an event to fire, or a method on the component instance.
In your case, I think defining a method on the Ractive.prototype would be the best way to have a common handler:
Ractive.prototype.getOptions = function( /* pass in arguments */ ){
// and/or this.event will give you access
// to current event and thus context
// you can also override this method in components and
// call this base method using this._super(..)
}
// now any ractive instance can use:
<select on-click="getOptions(data)">...</select>
An event based approach usually entails letting the root instance or common parent in the view hierarchy handle same event across child components:
var app = new Ractive({
template: "<componentA/><componentB/>",
oninit(){
this.on( '*.getOptions', ( event, arg ) => {
// any child component (at any depth)
// that fires a "getOptions" event will
// end up here
});
}
});
// in component A or B:
<select on-click="getOptions">...</select>
UPDATE: If you wanted to assign an event handler to the prototype, so in essence every component is pre-wired to handle an event of a set name, you could do:
Ractive.prototype.oninit = function(){
this.on( 'getOptions', ( event ) => {
// handle any "getOptions" event that happens in the instance
});
}
Just be aware that you must call this._super(); in any component in which you also implement oninit:
var Component = Ractive.extend({
oninit() {
// make sure we call the base or event listener won't happen!
this._super();
// do this component instances init work...
}
}

Resources