React - Provide Global Singleton From Composition Root - reactjs

To start off, I have been working with React now for three months and the application I am building is testable, performant, etc... Nothing wrong. My experience pre-React is from the Angular world and what is considered a best practice there is not normally in react and vice-a-versa... I don't think what I am doing is wrong for the application I am building also don't want to miss anything big.
To make a long story short, inside of my App.tsx (using TypeScript) file I am creating a new instance of a singleton service and exporting it as a named export. For example, my app component looks something like:
import * as React from 'react'
... axios import here
import { HttpService } from './core/http.service';
import { Spinner } from './shared/spinner';
const axiosInstance = Axios.create({config here});
const httpService = new HttpService(axiosInstance);
class App extends React.Component {
props: any;
state: any;
constructor(props: any) {
super(props);
}
render() {
return(<App Root Component Here...>)
}
}
export { httpService };
export default App;
Imagine a component somewhere in the app that needs to use my singleton service. For the purposes of my question, I will call the component Home (home/Home.tsx).
import * as React from 'react'
import { httpService } from '../App';
class Home extends React.Component {
props: HomeProps;
state: HomeState;
constructor(props: HomeProps) {
super(props);
this.state = {
isLoading: false,
myData: []
}
this.loadData = this.loadData.bind(this);
}
componentDidMount() {
this.loadData();
}
// Using httpService here.
loadData() {
this.setState({isLoading: true});
httpService.get('/api/somedataurl').then((response) => {
const { data } = response;
this.setState({myData: data});
}).then(() => {
this.setState({isLoading: false});
});
}
myDataList() {
return (<ul>...{map of this.state.myData}</ul>)
}
render() {
return this.state.isLoading ? (<Spinner>) : this.myDataList();
}
}
export default Home;
I decided to use this implementation because I know that I can always rely on the App component to be available and there are no plans for server-side rendering.
As a simple solution, is there anything seriously flawed with providing my singleton service as a named export and importing into other components as needed?

Related

Do I have to use a constructor when importing an external module I made in React?

I made a module in react.
So, I imported the module. And then, the function of the external module was called using the constructor.
import { connect } from './api';
...
class App extends Component {
constructor(props) {
super(props);
connect(message => {
console.log(message);
});
}
render(){
...
}
}
But I would like to express class fields syntax without using a constructor.
import { connect } from './api';
...
class App extends Component {
connect(message => {
console.log(message);
});
render(){
...
}
}
The results of the above code, 'connect' function is not executed because 'connect' is not declared.
Can't I get an function of external module without a constructor?
Here is your connect.js:
export const connect = message => {
console.log(message);
};
Here is your component:
import React from 'react';
import { connect } from './connect';
class App extends React.Component {
componentDidMount() {
connect('connected');
}
render() {
return (
<div>
<h1>Some Text...</h1>
</div>
);
}
}
export default App;
Should be fairly clear... If you have a question ask...

React TS: Despite passing method through wrapper, child still can't access getPage()

I'm trying to build a fetch method that can be shared to a bunch of Reader components through a higher order component. I believe I've built the HOC right, but I'm not 100% sure.
import React, { Component } from 'react';
import base from "./firebase";
export default (ChildComponent) => {
class GetPage extends Component<{},any> {
constructor(props: any) {
super(props);
this.state = {
text: "Hii"
};
}
public getPage(page: string) {
base
.fetch(page, { context: this, })
.then(data => this.setState({ text: data }));
console.log(this.state.text)
}
public render() {
return <ChildComponent getPage={this.getPage} text={...this.state.text} {...this.props}/>;
}
}
return GetPage;
};
You can see that I'm importing the HOC on the second line , but despite this, the 'Reader' component is throwing an error that 'getPage' is no where to be found.
import * as React from "react";
import GetPage from "./fetch";
class Reader extends React.Component<{},any>{
public componentWillMount() {
this.getPage('1A1');
}
public render() {
return <div{...getPage('1A1')}>{...this.state.text}</div>;
}
}
export default (GetPage(Reader));
Inside your Reader component instead of accessing this.getpage try with this.props.getpage
and I don't understand why you are doing with following:
<div{...getPage('1A1')}>

How to pass a global object around React Native components?

I am trying to set up a messaging module in my React Native app, which should get information from a service and render it in different components in different ways. Kind of like the inbox messages here: you receive a message, and in the header component you see the inbox with a red dot and the number of new messages. If you click it, you go to another component that renders the messages fully.
Now, I created two components to render the inbox in those two different ways. But when I try link them to the class that handles the notifications, I get errors inside the components classes saying that the object is undefined.
I have something like this:
Class that stores new messages
class Notifications {
constructor() {
this.notifications = [];
}
receiveNotification(notif) {
this.notifications.push(notif);
}
}
let notifications = new Notifications();
export { notifications };
Class that handles new messages from service
import framework from 'framework'; // this is the framework I use to communicate with the service
import Notifications from './Notifications.js';
export class PushNotificator extends Component {
constructor(props) {
super(props);
this.state = {
token: ""
}
}
componentDidMount() {
framework.requestPermissions()
.then(() => console.log('granted'))
.catch(() => console.log('notification permission rejected'));
framework.getToken().then(token => {
console.log("TOKEN (getToken)", token);
this.setState({token: token});
});
this.notificationListener = framework.on(frameworkEvent.Notification, notif => {
console.log("Notification", notif);
this.showLocalNotification(notif);
})
}
showLocalNotification(notif) {
Notifications.notifications.push(notif); // this fails because Notifications is undefined
framework.presentLocalNotification({
title: notif.title,
body: notif.body,
priority: "high",
click_action: notif.click_action,
show_in_foreground: true,
local: true
});
}
componentWillUnmount() {
this.notificationListener.remove();
}
render() {
return null;
}
}
Relevant part of the header inbox component
import Notifications from './Notifications.js' //assume the paths are correct
import {PushNotificator} from './PushNotificator.js'
export class Home extends Component {
constructor(props) {
super(props);
this.state = {
loading: true,
notifications: Notifications.notifications.find(notif => notif.seen).length
};
this.closeActivityIndicator = () => setTimeout(() => {
this.setState({ loading: false });
}, 2000);
}
...
render() {
<PushNotificator />
...
}
As soon as the constructor is called, the program fails because Notifications is undefined. But why is it undefined? Can I not use it this way?
Thanks.
There are two options, I see, how to fix your issue:
1. You have already instantiated your Notifications, so it is possible to export that instance by default without additional wrapping:
export default notifications;
and then just:
import notifications from './Notifications.js';
// ...
notifications.push(notif);
2. If you don't want to use the default, you may continue exporting your instance via
export { notifications };
and in that case you need to import it properly:
import { notifications } from './Notifications.js';
// ...
notifications.push(notif);
But in both cases you are working with instatiated notifications object, not with Notifications class.

Meteor React Komposer: Execute Component Constructor AFTER ready subscription

I have a React Component Post:
export class Post extends React.Component {
constructor(props) {
this.state = this.props;
}
...
}
, which I compose with
import { composeWithTracker } from 'react-komposer';
import { composeWithTracker } from 'react-komposer';
import Post from '../components/post.jsx';
function composer(props, onData) {
const subscription = Meteor.subscribe('post', props.postId);
if (subscription.ready()) {
const data = {
ready: true,
posts: Posts.findOne(props.postId).fetch()
}
onData(null, data);
} else {
onData(null, {ready: false});
}
}
export default composeWithTracker(composer)(Post);
. As given in the Post Component I want to put some properties in the state of the component, but the constructor will be executed before I get the data from the composer!
How do I wait until the data is send and then put my props into the state?
Isn't this what the React Kompose should do? BTW I am using Version 1.~ to get composeWithTracker.
You could use componentWillReceiveProps to get new properties and set as component state. This function will run whenever there are new properties passed to component:
export class Post extends React.Component {
// ...
componentWillReceiveProps(nextProps) {
this.setState({
...nextProps,
});
}
// ...
}

Can't find an internal method in a React container component

I'm trying to get AJAX-retrieved data into a parent React component so it can be fed down to a child component. I'm using the popular pattern for this defined here where a comment list is used as the example:
components/CommentList.js
import React from 'React';
export class CommentList extends React.Component {
constructor(props) {
super(props);
}
render() {
return <ul> {this.props.comments.map(renderComment)} </ul>;
}
renderComment({body, author}) {
return <li>{body}—{author}</li>;
}
}
components/CommentListContainer.js
import React from 'React';
import { CommentList } from './CommentList';
export class CommentListContainer extends React.Component {
constructor() {
super();
this.state = { comments: [] }
}
componentDidMount() {
$.ajax({
url: "http://get/some/api",
dataType: 'json',
success: function(comments) {
this.setState({comments: comments});
}.bind(this)
});
}
render() {
return <CommentList comments={this.state.comments} />;
}
}
index.js: the entry point for webpack
import React from 'react'
import { render } from 'react-dom'
import { CommentListContainer } from './components/CommentListContainer';
window.React = React;
render(
<CommentListContainer />,
document.getElementById('nav__react-target')
)
When doing all this, I get the following error:
Uncaught ReferenceError: renderComment is not defined
I've move the methods around as well as tweaked the importing of dependencies in various spots with no luck. Any ideas?
Thanks in advance.
You don't have unguarded references to sibling methods with ES2015 classes (as you do in Java / C#, etc.) - instead you need to explicitly reference this to get at the methods of the class:
render() {
// I changed map(renderComment) to map(this.renderComment)
return <ul>{this.props.comments.map(this.renderComment)}</ul>;
}

Resources