ReactJS: re-base helper filer for collection method calls - javascript

I have struggled a lot trying to figure out how to combine ReactJS and firebase (3).
Luckily I found the awesome re-base repo, which is awesome and exactly what I want.
fb-config.js
var Rebase = require('re-base');
var base = Rebase.createClass({
apiKey: "apiKey",
authDomain: "projectId.firebaseapp.com",
databaseURL: "https://databaseName.firebaseio.com",
storageBucket: "bucket.appspot.com",
messagingSenderId: "xxxxxxxxxxxxxx"
});
export default base;
app.js
import React, { Component } from 'react';
import base from 'fb-config';
import ExampleComponent from './components/ExampleComponent';
class App extends Component {
constructor() {
super();
// getInitialState
this.state = {
DB: {}
};
}
componentDidMount() {
this.ref = base.syncState('demo',
{ context: this,
state: 'DB' }
);
}
componentWillUnmount() {
base.removeBinding(this.ref);
}
render() {
return (
<ExampleComponent />
)
}
}
export default App
components/ExampleComponent.js
import React, { Component } from 'react';
import base from '../fb-config';
class ExampleComponent extends Component {
constructor(props, context){
super(props, context);
this.pushing = this.pushing.bind(this);
}
pushing() {
const now = new Date().getTime();
let data = {};
data[now.toString(16)] = 'Hello World';
base.fetch('demo', {
context: this,
asArray: false
}).then( snapshot => {
data = {... snapshot, ...data};
base.post('demo', {data})
.then( () = { console.log("pushed") });
});
}
render() {
return (
<button onClick={this.pushing}>PUSH</button>
)
}
};
export default ExampleComponent;
This setup works. But I would like to move the pushing method to another file (like fb-commands.js). How would I do that?

You can move pushing as-is, all you need is to pass context to the function, since it wont have access to the component instance via this.
// fb-commands.js
export const pushing = (context) => {
const now = new Date().getTime();
let data = {};
data[now.toString(16)] = 'Hello World';
base.fetch('demo', {
context: context,
asArray: false
}).then( snapshot => {
data = {... snapshot, ...data};
base.post('demo', {data})
.then(() => console.log("pushed"));
});
};
// components/ExampleComponent.js
import React, { Component } from 'react';
import base from '../fb-config';
import {pushing} from 'path/to/fb-commands';
class ExampleComponent extends Component {
render() {
return (
<button onClick={() => pushing(this)}>PUSH</button>
);
}
}
export default ExampleComponent;

Based on my experience, I can think of 3 approaches (maybe there are more).
Approach #1: Via ES6 Import / Export (Simple)
ArneHugo describes it well in his answer.
Just export pushing function to a module and then import it and use it directly. Pass the this context or any other params needed.
Sounds awesome to me if you want to share utility functions.
Approach #2: Via Mixins.
That's not a good practice.
Plus, mixins are not supported when using React components written in ES6 syntax. Moreover, they will not have support for ES6 classes in React. The reason is that they are considered harmful.
So, mixins are on their way out.
Approach #3: Via Composition (Advanced)
That is the best option if you need to implement something more advanced then just re-using common methods. Let's say if you want:
to reuse logic and bootstrap abstraction
to render Highjacking
to do State abstraction and manipulation
to do Props manipulation
Then, enter Composition and Higher-Order Components!
A higher-order component is just a function that takes an existing component and returns another component that wraps it.
You can use this approach to wrap the component and not only share common code / logic, but share data, manipulate state, props and etc.
More on this topic you can read on the franleplant's great post here.
Conclusion
If you need just a helper method abstraction - use Approach #1.
Never use Approach #2.
If you want something more then that - consider applying Approach #3.

Related

How to unit test a higher order component using jest and enzyme in react

I am currently trying to write a test to test what is inside of a higher order components
my test like so:
let Component = withEverything(Header);
let wrapper;
it('renders correctly', async () => {
wrapper = await mountWithSleep(
<Component componentProps={{ session: { id: '2' } }} />,
0.25
);
console.log(wrapper.debug());
});
});
outputs the following:
<Component>
<WithSession component={[Function: GlobalNav]} innerProps={{...}} />
</Component>
My with session file looks like the following:
import React, { Component, ComponentType } from 'react';
import { Session, session } from '#efa/web/src/modules/auth/authService';
import { Omit } from '#everlutionsk/helpers';
import { Subscription } from 'rxjs';
class WithSession extends Component<Props, State> {
state: State = {
session: undefined
};
private subscription: Subscription;
componentDidMount() {
this.subscription = session.subscribe(session => {
this.setState({ session });
});
}
componentWillUnmount() {
this.subscription.unsubscribe();
}
render() {
if (this.state.session === undefined) return null;
const Component = this.props.component;
const props = { ...this.props.innerProps, session: this.state.session };
return <Component {...props} />;
}
}
/**
* Injects a current session to the given [component].
*/
export function withSession<P extends SessionProps>(
component: ComponentType<P>
): ComponentType<Omit<P, keyof SessionProps>> {
return props => <WithSession component={component} innerProps={props} />;
}
export interface SessionProps {
readonly session: Session | null;
}
interface Props {
readonly component: ComponentType;
readonly innerProps: any;
}
interface State {
readonly session: Session | null | undefined;
}
I have tried to do a jest.mock which gets me part of the way using this:
jest.mock('#efa/web/src/modules/auth/components/withSession', () => {
//#ts-ignore
const original = jest.requireActual(
'#efa/web/src/modules/auth/components/withSession'
);
return {
__esModule: true,
...original,
withSession: component => {
return component;
}
};
});
Using this module i can at least see a returned component instead of with session. But now the issue is i need to be able to set the session state. wondering if anyone can help?!
It is worth noting this is a project which we inherited i would not of implemented it this way ever!
You're correct that this type of code absolutely makes testing more difficult. The "best" way in this case its probably a bit of work because the nicest way to go about it would be to switch this thing to context; and then add options to your test framework mount method to populate that context how you want from individual tests. Though, you can reach something similar with old fashioned HOCs.
There's a cheaper way, and that would be to allow options to be passed to withEverything (if this is used by the app as well, you can create a mirror one called withTestHocs or similiar. I.e.
withTestHocs(Header, {
session: //... object here
})
Internally, this HOC would no longer call withSession whatsoever. Instead, it would call a HOC who's only purpose is to inject that session config object into the component for test reasons.
There's no reason to do complex mocking to get the session right on every test, its a waste of time. You only need that if you're actually testing withSession itself. Here you should be prioritising your test framework API that makes having custom session per test nice and simple. jest.mock is not easily parametrised, so that in itself is also another good reason to not go down that road. Again, the exception is when you're unit testing the actual session hoc but those tests are typically quite edge-casey and wont be using the core "test framework HOC" you'll use for all your userland/feature code -- which is what I'm focusing on here.
Note with this solution, you wouldn't need the complex jest mocking anymore (provided all tests were moved to the new way).
export const withEverything =
(Component, {session}) =>
({ providerProps, componentProps }) =>
(
<MockedProvider {...providerProps}>
<BrowserRouter>
<FlashMessage>
<Component {...componentProps} session={session} />
</FlashMessage>
</BrowserRouter>
</MockedProvider>
);
Now in your test
it('renders correctly', async () => {
wrapper = await mountWithSleep(
const withEverything(Header, { session: { id: '2' }}),
0.25
);
console.log(wrapper.debug());
});
If you need to be able to manipulate the session mid-test, you could do that by returning a method from withEverthing that allows the session to be set, but im not sure if you need it.

How to use mobx-react 'observer' without decorator syntax?

I am trying to shoe horn mobx into a vr application I am making with react 360. I've tried to use the decorator syntax but after wasting a solid chunk of time trying to implement it, I've decided to use the nondecorator syntax. Here is an example that I came across from the mobx documentation that I have a question on. Here is the code:
import {observer} from "mobx-react";
var timerData = observable({
secondsPassed: 0
});
setInterval(() => {
timerData.secondsPassed++;
}, 1000);
#observer class Timer extends React.Component {
render() {
return (<span>Seconds passed: { this.props.timerData.secondsPassed } </span> )
}
};
ReactDOM.render(<Timer timerData={timerData} />, document.body);
Notice the observer declaration on the Timer class. The documentation states this.
Note that using #observer as decorator is optional, observer(class Timer ... { }) achieves exactly the same.
Would this be the correct way of implementing Timer?
observer(class Timer extends React.Component {
render() {
return (<span>Seconds passed: { this.props.timerData.secondsPassed } </span> )
}
})
Concerning the code snippet you have added I don't know whether it is a valid way or not, but here's the way I'm using MobX without the decorator syntax in my application project:
Create your MobX store, say MyStore.js like the following:
import {observable, action, computed, decorate} from 'mobx';
export default class MyStore {
storeMap = observable(new Map());
storeArray = observable([]);
storeBoolean = false
get storeMapSize() {
return this.storeMap.size;
}
setStoreBoolean(value) {
this.storeBoolean = value;
}
}
decorate(MyStore, {
storeMap: observable,
storeArray: observable,
storeBoolean: observable
storeMapSize: computed,
setStoreBoolean: action
});
Then in your component Timer.js:
import {inject, observer} from "mobx-react";
class Timer extends React.Component {
render() {
return (<span>value from store: { this.props.myStore.storeMap.get('any_key') } </span> )
}
}
export default inject('myStore')(observer(Timer));
and you can create as many stores as you want and inject all of them to your components using the same inject method and use them in the same way via this.props, for example
export default inject('myStore', 'anotherStore', 'anotherNewStore')(observer(Timer));

Reactjs ES6 Access function outside of class

I have a class "MessageDisplay" of which I want to call the function sendMassageToServer from the outside. I´ve built a helper function to call from another file. But how do you export functions that are inside classes?
These data are just examples.
main.js
export function sendSpeechToServer(query){
MessageDisplay.sendMessageToServer(query);
}
class MessageDisplay extends React.Component {
constructor(props) {
super(props);
this.state = {message : []};
}
(export const??) sendMessageToServer(searchQuery) {
...code
}
}
We are accesing the sendSpeechToServer() function from another file. Unortunately I am not even able to reach sendMessageToServer() from inside sendSpeechToServer().
Any help surely is appreciated. :)
EDIT:
The answer is found. For any other people:
export function sendSpeechToServer(query){
let test = new MessageDisplay();
test.sendMessageToServer(query);
}
Better way to separate component(MessageDisplay) and sendMessageToServer.
Then you can import sendMessageToServer awry where. You can inject sendMessageToServer like a props:
// main.js
import { sendMessageToServer } from './api';
<MessageDisplay sendMessage={sendMessageToServer} />
// MessageDisplay.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class MessageDisplay extends Component {
static propTypes = {
sendMessage: PropTypes.func.isRequired,
}
handleClick = (e) => {
e.preventDefault();
this.props.sendMessage();
};
render() {
return (<button onClick={this.handleClick}>Send to</button>)
}
}
export default MessageDisplay;
It useful for testing.
Instantiating a component manually for general purposes like let test = new MessageDisplay() is an antipattern, this indicates that a class is misused.
React component classes are primarily intended to make lifecycle hooks available and maintain state. They can sparsely benefit from inheritance (besides the relationship with React.Component) and other OOP traits.
The fact that it's possible to use component method as new MessageDisplay().sendMessageToServer(query) means that it was a mistake to make it component method in the first place. Classes aren't supposed to be glorified namespaces; ES modules play the role of namespaces in modern JavaScript.
A proper way is to extract the method and use it in both places as regular helper function. Functional approach is considered idiomatic in React.
export function sendSpeechToServer(query){
let test = new MessageDisplay();
test.sendMessageToServer(query);
}
it is bad, because you should not, create new react.component instance with new keyword,
better use static function like this
static sendMessageToServer(searchQuery) {
...code
}
and then
export function sendSpeechToServer(query){
MessageDisplay.sendMessageToServer(query);
}

How to create singleton class when I importing it from multiple places?

How can I make a Singleton class if I want to import it at multiple places?
I ended up with something like this, but I am exporting a new() instance (at least I think so) at each time I import it.
class RenderToRootAPI {
renderToRootComponent = null
setRenderComponent = ref => this.renderToRootComponent = ref
setInstance = instance => {
console.warn('setting instance')
this.renderToRootComponent.setInstance(instance)
}
deleteInstance = () => this.renderToRootComponent.deleteInstance
}
export default new RenderToRootAPI()
What you have written will export a singleton. It doesn't matter how many times you import it.
It might look a bit more clear if you write it like this as an example:
class RenderToRootAPI {
renderToRootComponent = null
setRenderComponent = ref => this.renderToRootComponent = ref
setInstance = instance => {
console.warn('setting instance')
this.renderToRootComponent.setInstance(instance)
}
deleteInstance = () => this.renderToRootComponent.deleteInstance
}
const renderToRootAPI = new RenderToRootAPI();
export default renderToRootAPI;
The class is not even exported, and the single exported instance will be used in all the modules that import it.

How to test decorated React component with shallow rendering

I am following this tutorial: http://reactkungfu.com/2015/07/approaches-to-testing-react-components-an-overview/
Trying to learn how "shallow rendering" works.
I have a higher order component:
import React from 'react';
function withMUI(ComposedComponent) {
return class withMUI {
render() {
return <ComposedComponent {...this.props}/>;
}
};
}
and a component:
#withMUI
class PlayerProfile extends React.Component {
render() {
const { name, avatar } = this.props;
return (
<div className="player-profile">
<div className='profile-name'>{name}</div>
<div>
<Avatar src={avatar}/>
</div>
</div>
);
}
}
and a test:
describe('PlayerProfile component - testing with shallow rendering', () => {
beforeEach(function() {
let {TestUtils} = React.addons;
this.TestUtils = TestUtils;
this.renderer = TestUtils.createRenderer();
this.renderer.render(<PlayerProfile name='user'
avatar='avatar'/>);
});
it('renders an Avatar', function() {
let result = this.renderer.getRenderOutput();
console.log(result);
expect(result.type).to.equal(PlayerProfile);
});
});
The result variable holds this.renderer.getRenderOutput()
In the tutorial the result.type is tested like:
expect(result.type).toEqual('div');
in my case, if I log the result it is:
LOG: Object{type: function PlayerProfile() {..}, .. }
so I changed my test like:
expect(result.type).toEqual(PlayerProfile)
now it gives me this error:
Assertion Error: expected [Function: PlayerProfile] to equal [Function: withMUI]
So PlayerProfile's type is the higher order function withMUI.
PlayerProfile decorated with withMUI, using shallow rendering, only the PlayerProfile component is rendered and not it's children. So shallow rendering wouldn't work with decorated components I assume.
My question is:
Why in the tutorial result.type is expected to be a div, but in my case isn't.
How can I test a React component decorated with higher order component using shallow rendering?
You can't. First let's slightly desugar the decorator:
let PlayerProfile = withMUI(
class PlayerProfile extends React.Component {
// ...
}
);
withMUI returns a different class, so the PlayerProfile class only exists in withMUI's closure.
This is here's a simplified version:
var withMUI = function(arg){ return null };
var PlayerProfile = withMUI({functionIWantToTest: ...});
You pass the value to the function, it doesn't give it back, you don't have the value.
The solution? Hold a reference to it.
// no decorator here
class PlayerProfile extends React.Component {
// ...
}
Then we can export both the wrapped and unwrapped versions of the component:
// this must be after the class is declared, unfortunately
export default withMUI(PlayerProfile);
export let undecorated = PlayerProfile;
The normal code using this component doesn't change, but your tests will use this:
import {undecorated as PlayerProfile} from '../src/PlayerProfile';
The alternative is to mock the withMUI function to be (x) => x (the identity function). This may cause weird side effects and needs to be done from the testing side, so your tests and source could fall out of sync as decorators are added.
Not using decorators seems like the safe option here.
Use Enzyme to test higher order / decorators with Shallow
with a method called dive()
Follow this link, to see how dive works
https://github.com/airbnb/enzyme/blob/master/docs/api/ShallowWrapper/dive.md
So you can shallow the component with higher order and then dive inside.
In the above example :
const wrapper=shallow(<PlayerProfile name={name} avatar={}/>)
expect(wrapper.find("PlayerProfile").dive().find(".player-profile").length).toBe(1)
Similarly you can access the properties and test it.
You can use 'babel-plugin-remove-decorators' plugin. This solution will let you write your components normally without exporting decorated and un-decorated components.
Install the plugin first, then create a file with the following content, let us call it 'babelTestingHook.js'
require('babel/register')({
'stage': 2,
'optional': [
'es7.classProperties',
'es7.decorators',
// or Whatever configs you have
.....
],
'plugins': ['babel-plugin-remove-decorators:before']
});
and running your tests like below will ignore the decorators and you will be able to test the components normally
mocha ./tests/**/*.spec.js --require ./babelTestingHook.js --recursive
I think the above example is confusing because the decorator concept is used interchangeably with idea of a "higher order component". I generally use them in combination which will make testing/rewire/mocking easier.
I would use decorator to:
Provide props to a child component, generally to bind/listen to a flux store
Where as I would use a higher order component
to bind context in a more declarative way
The problem with rewiring is I don't think you can rewire anything that is applied outside of the exported function/class, which is the case for a decorator.
If you wanted to use a combo of decorators and higher order components you could do something like the following:
//withMui-decorator.jsx
function withMUI(ComposedComponent) {
return class withMUI extends Component {
constructor(props) {
super(props);
this.state = {
store1: ///bind here based on some getter
};
}
render() {
return <ComposedComponent {...this.props} {...this.state} {...this.context} />;
}
};
}
//higher-order.jsx
export default function(ChildComp) {
#withMui //provide store bindings
return class HOC extends Component {
static childContextTypes = {
getAvatar: PropTypes.func
};
getChildContext() {
let {store1} = this.props;
return {
getAvatar: (id) => ({ avatar: store1[id] });
};
}
}
}
//child.js
export default Child extends Component {
static contextTypes = {
getAvatar: PropTypes.func.isRequired
};
handleClick(id, e) {
let {getAvatar} = this.context;
getAvatar(`user_${id}`);
}
render() {
let buttons = [1,2,3].map((id) => {
return <button type="text" onClick={this.handleClick.bind(this, id)}>Click Me</button>
});
return <div>{buttons}</div>;
}
}
//index.jsx
import HOC from './higher-order';
import Child from './child';
let MyComponent = HOC(Child);
React.render(<MyComponent {...anyProps} />, document.body);
Then when you want to test you can easily "rewire" your stores supplied from the decorator because the decorator is inside of the exported higher order component;
//spec.js
import HOC from 'higher-order-component';
import Child from 'child';
describe('rewire the state', () => {
let mockedMuiDecorator = function withMUI(ComposedComponent) {
return class withMUI extends Component {
constructor(props) {
super(props);
this.state = {
store1: ///mock that state here to be passed as props
};
}
render() {
//....
}
}
}
HOC.__Rewire__('withMui', mockedMuiDecorator);
let MyComponent = HOC(Child);
let child = TestUtils.renderIntoDocument(
<MyComponent {...mockedProps} />
);
let childElem = React.findDOMNode(child);
let buttons = childElem.querySelectorAll('button');
it('Should render 3 buttons', () => {
expect(buttons.length).to.equal(3);
});
});
I'm pretty sure this doesn't really answer your original question but I think you are having problems reconciling when to use decorators vs.higher order components.
some good resources are here:
http://jaysoo.ca/2015/06/09/react-contexts-and-dependency-injection/
https://medium.com/#dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750
https://github.com/badsyntax/react-seed/blob/master/app/components/Menu/tests/Menu-test.jsx
https://github.com/Yomguithereal/baobab-react/blob/master/test/suites/higher-order.jsx
In my case decorators are very useful and I dont want to get rid of them (or return wrapped and unwrapped versions) im my application.
The best way to do this in my opinion is to use the babel-plugin-remove-decorators (which can be used to remove them in tests) has Qusai says, but I wrote the pre-processor differently like below:
'use strict';
var babel = require('babel-core');
module.exports = {
process: function(src, filename) {
// Ignore files other than .js, .es, .jsx or .es6
if (!babel.canCompile(filename)) {
return '';
}
if (filename.indexOf('node_modules') === -1) {
return babel.transform(src, {
filename: filename,
plugins: ['babel-plugin-remove-decorators:before']
}).code;
}
return src;
}
};
Take notice of the babel.transform call that im passing the babel-plugin-remove-decorators:before element as an array value, see: https://babeljs.io/docs/usage/options/
To hook this up with Jest (which is what I used), you can do it with settings like below in your package.json:
"jest": {
"rootDir": "./src",
"scriptPreprocessor": "../preprocessor.js",
"unmockedModulePathPatterns": [
"fbjs",
"react"
]
},
Where preprocessor.js is the name of the preprocessor.

Categories

Resources