All Projects → isoreact → core

isoreact / core

Licence: MIT License
A library for making isomorphic React components with RxJS

Programming Languages

javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to core

mutoid
Reactive library for data fetching, caching, state management
Stars: ✭ 24 (+100%)
Mutual labels:  rxjs, isomorphic
Preact Redux Isomorphic
preact-redux-isomorphic PWA SPA SSR best practices and libraries in under 80kB page size (for live demo click the link below)
Stars: ✭ 85 (+608.33%)
Mutual labels:  rxjs, isomorphic
vue-rxjs
Yet another rxjs binding library for Vue.js
Stars: ✭ 14 (+16.67%)
Mutual labels:  rxjs
Contemplate
Contemplate: Fast, extendable object-oriented and light-weight Template Engine for PHP, Python, Node.js, Browser and XPCOM/SDK JavaScript
Stars: ✭ 15 (+25%)
Mutual labels:  isomorphic
react-rxjs-state-management-hoc-example
Showcasing how RxJS can work in React with state management and higher-order components.
Stars: ✭ 14 (+16.67%)
Mutual labels:  rxjs
rxzu
RxZu Diagrams
Stars: ✭ 40 (+233.33%)
Mutual labels:  rxjs
quranicaudio-app
QuranicAudio.com Mobile App
Stars: ✭ 44 (+266.67%)
Mutual labels:  isomorphic
rxjs-ninja
RxJS Operators for handling Observable strings, numbers, booleans and more
Stars: ✭ 68 (+466.67%)
Mutual labels:  rxjs
isomorphic-relay-boilerplate
No description or website provided.
Stars: ✭ 30 (+150%)
Mutual labels:  isomorphic
rx-ease
Spring animation operator for rxjs 🦚✨
Stars: ✭ 16 (+33.33%)
Mutual labels:  rxjs
bind-observable
Provides a typescript decorator which binds class properties to observable companion properties.
Stars: ✭ 15 (+25%)
Mutual labels:  rxjs
todo-list
TodoList using Ionic2/3 & Firebase: * PWA * SSO Google plus. * Share list via QRcode. * Upload image from Camera or Storage. * Speech Recognition.
Stars: ✭ 18 (+50%)
Mutual labels:  rxjs
reactive-store
A store implementation for state management with RxJS
Stars: ✭ 29 (+141.67%)
Mutual labels:  rxjs
fetch
Isomorphic Wordpress API client and React hooks - super tiny, super fast.
Stars: ✭ 47 (+291.67%)
Mutual labels:  isomorphic
angular-minesweeper
Play a different version of the classic Minesweeper game built with Angular
Stars: ✭ 54 (+350%)
Mutual labels:  rxjs
rx-ipc-electron
Pass RxJS Observables through IPC in Electron
Stars: ✭ 28 (+133.33%)
Mutual labels:  rxjs
javascript-fun
source code generator for www.javascript.fun
Stars: ✭ 32 (+166.67%)
Mutual labels:  rxjs
signalr-client
SignalR client library built on top of @aspnet/signalr. This gives you more features and easier to use.
Stars: ✭ 48 (+300%)
Mutual labels:  rxjs
anabneri
✨ This is a README that appears on the homepage of my profile.
Stars: ✭ 14 (+16.67%)
Mutual labels:  rxjs
ng2-rest
Isomorphic, simple, robust REST API library for Browser and NodeJS, ( ts / js ) apps
Stars: ✭ 26 (+116.67%)
Mutual labels:  isomorphic

IsoReact · GitHub license npm version npm Build Status Gitter PRs Welcome

Important: This library has been deprecated in favor of individual libraries, each focusing on a different state management library, starting with Bacon.js 1.x in @isoreact/bacon1.

IsoReact is a small library to use React and RxJS together to build isomorphic React components.

Features:

  • Server-side render fully populated components (not just loading states).
  • Hydrate server-side rendered components without hitting APIs for the initial client-side render.
  • State management with RxJS.
  • Connect state to the component tree via the new context API.

This is a library, not a framework. It is focused on state management and isomorphism and should otherwise stay out of your way.

Installation

npm i -S @isoreact/core

Examples

See isoreact/example

Usage

Create a context to connect an RxJS event stream to your React component:

// profile-context.js
import React from 'react';

export default React.createContext();

Create your React component hierarchy, connecting your context to it using <Connect context={yourContext} />:

// profile.js
import React from 'react';
import {Connect} from '@isoreact/core';
import profileContext from './profile-context';

const ProfileName = () => (
    <section className="profile__name">
        <Connect
            context={profileContext}
            distinctBy={({name}) => name}
        >
            {({name}) => name}
        </Connect>
    </section>
);

const ProfilePhoto = () => (
    <section className="profile__photo">
        <Connect context={profileContext}>
            {({photo}) => (
                <img className="profile__photo-img" src={photo} />
            )}
        </Connect>
    </section>
);

const Profile = () => (
    <section className="profile">
        <ProfileName />
        <ProfilePhoto />
    </section>
);

export default Profile;

You might have noticed distinctBy={({name}) => name} in one of the <Connect /> elements. It's an optional function that uniquely identifies values we're interested in, which is used by Connect to skip duplicate values and therefore duplicate renders.

Define your component's event stream and make it isomorphic:

// iso-profile.js
import PropType from 'prop-types';
import {of as observableOf, combineLatest} from 'rxjs';
import {map} from 'rxjs/operators';
import {isomorphic} from '@isoreact/core';
import Profile from './profile';
import profileContext from './profile-context';
import fetchName from './streams/fetch-name';
import fetchPhoto from './streams/fetch-photo';

const IsoProfile = isomorphic({
    name: 'iso-profile',
    component: Profile,
    context: profileContext,
    getData: (props, hydration) => {
        const {userId} = props;
        
        const name$ = hydration
            ? observableOf({name: hydration.name})
            : fetchName(userId);
        
        const photo$ = hydration
            ? observableOf({photo: hydration.photo})
            : fetchPhoto(userId);
        
        return combineLatest(name$, photo$)
            .pipe(
                map(([name, photo]) => ({
                    // React component rendered with this state as its props
                    state: {
                        name,
                        photo,
                    },
                    
                    // Data rendered alongside the React element in the HTML page
                    hydration: {
                        name,
                        photo,
                    },
                })),
            );
    },
    propTypes: {
        userId: PropType.string.isRequired,
    },
});

export default IsoProfile;

The general contract of getData(props, hydration) is:

  • Return an observable that emits objects of the form {state, hydration} where both state and hydration are objects.
  • If getData is provided a hydration object, the observable is expected to immediately produce an event.
  • Events must contain hydration only when the hydration parameter is not present (i.e. on the server).
  • Events can contain hydration when the hydration parameter is present, but it will have no effect.
  • Keep hydration small to keep server-side rendered HTML pages small. Only attach the minimum amount of data required to hydrate isomorphic components without them having to fetch data from APIs.

Somewhere on the server:

import React from 'react';
import {renderToHtml} from '@isoreact/core';
import {IsoProfile} from './iso-profile';

// Server-side render an HTML page consisting of the profiles from a list of user IDs.
async function renderUserProfilesPage(userIds) {
    // Generate server-side rendered profile of users
    const htmlArray = await Promise.all(
        userIds.map((userId) => renderToHtml(<IsoProfile userId={userId} />))
    );

    return `<body>${htmlArray.join('')}</body>`;
}

When renderToHtml is called, it will call each isomorphic components' getData function, passing in the isomorphic component's props (in this case, userId). When the stream returned by getData produces its first event (an object consisting of state to inject into the React component and hydration to attach to the HTML page), the isomorphic component's React component will be rendered with the state as its props and the hydration data will be rendered adjacent to it in the HTML page.

Somewhere on the client:

import {hydrate} from '@isoreact/core';
import IsoProfile from './iso-profile';

// Hydrate all instances of iso-profile on the page
hydrate(IsoProfile);

When hydrate is called, it finds all the server-side rendered instances of the isomorphic component in the DOM, reads their attached props and hydration data, then calls getData(props, hydration), expecting the client to render the profiles synchronously, without having to load data from APIs.

Bear in mind that isomorphic components are just React components, so you can use them directly in JSX and you don't even need to initially render them on the server. You could even use this library just for connecting to RxJS.

Support for styled-components

The server-side rendering portion of the above example can be updated as follows:

import React from 'react';
import {renderToHtml, StyledComponentsServerRenderer} from '@isoreact/core';
import {IsoProfile} from './iso-profile';

// Server-side render an HTML page consisting of the profiles from a list of user IDs.
async function renderUserProfilesPage(userIds) {
    // Generate server-side rendered profile of users
    const renderer = new StyledComponentsServerRenderer();
    const htmlArray = await Promise.all(
        userIds.map((userId) => renderToHtml(
            <IsoProfile userId={userId} />,
            {renderer}
        ))
    );

    return `<head>${renderer.getStyleTags()}</head><body>${htmlArray.join('')}</body>`;
}

This uses StyledComponentsServerRenderer as an alternative renderer, which uses ServerStyleSheet from styled-components to gather rendered stylesheets.

Contributing

Contributions are welcome. See CONTRIBUTING.md in this repo for contribution guidelines.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].