Coder Social home page Coder Social logo

fanduel-oss / refract Goto Github PK

View Code? Open in Web Editor NEW
813.0 25.0 28.0 1.7 MB

Harness the power of reactive programming to supercharge your components

Home Page: https://refract.js.org

License: MIT License

JavaScript 15.01% TypeScript 84.99%
javascript reactive-programming react preact inferno redux rxjs callbag most xstream

refract's Introduction


Handle your component effects and side-effects in a clear and declarative fashion
by using asynchronous data streams (reactive programming).


Why? · Install · The Gist · Learn · Contribute · Discuss


Build MIT License Styled with Prettier


  • 🎳 Decentralised: attach effects and side-effects to your components, for better code splitting results
  • 🌅 Gradual: use on an existing component today, throughout your app tomorrow
  • 🚀 Reactive: leverage the power and benefits of reactive programming
  • 💾 Tiny: less than 2Kb minified and gzipped
  • Typed: written in TypeScript, fully typed integrations
  • Universal: supports React, React Native, Inferno and Preact

Refract makes reactive programming possible in React, React Native, Preact and Inferno, with only a single higher-order component or a single hook! You can choose to start using a tiny bit of reactive programming, or go full reactive. Refract allows you to:

We also provide a Redux integration, which can serve as a template for integrations with other libraries. With a single HoC, you can fully replace libraries like recompose, redux-observable, and react-redux to name a few!

Why?

Component-based architecture and functional programming have become an increasingly popular approach for building UIs. They help make apps more predictable, more testable, and more maintainable.

However, our apps don't exist in a vacuum! They need to handle state, make network requests, handle data persistence, log analytics, deal with changing time, and so on. Any non-trivial app has to handle any number of these effects. Wouldn't it be nice to cleanly separate them from our apps?

Refract solves this problem for you, by harnessing the power of reactive programming. For an in-depth introduction, head to Why Refract.

Installation

Refract is available for a number of reactive programming libraries. For each library, a Refract integration is available for React, Inferno, Preact and Redux.

Available packages:

React Inferno Preact Redux
Callbag refract-callbag refract-inferno-callbag refract-preact-callbag refract-redux-callbag
Most refract-most refract-inferno-most refract-preact-most refract-redux-most
RxJS refract-rxjs refract-inferno-rxjs refract-preact-rxjs refract-redux-rxjs
xstream refract-xstream refract-inferno-xstream refract-preact-xstream refract-redux-xstream

To use the latest stable version, simply npm install the package you want to use:

npm install --save refract-rxjs

The Gist

The example below uses refract-rxjs to send data to localstorage.

Every time the username prop changes, its new value is sent into the stream. The stream debounces the input for two seconds, then maps it into an object (with a type of localstorage) under the key value. Each time an effect with the correct type is emitted from this pipeline, the handler calls localstorage.setItem with the effect's name and value properties.

const aperture = component => {
    return component.observe('username').pipe(
        debounce(2000),
        map(username => ({
            type: 'localstorage',
            name: 'username',
            value: username
        }))
    )
}

const handler = initialProps => effect => {
    switch (effect.type) {
        case 'localstorage':
            localstorage.setItem(effect.name, effect.value)
            return
    }
}

const WrappedComponent = withEffects(aperture, { handler })(BaseComponent)

The example demonstrates uses the two building blocks used with Refract - an aperture and a handler - and shows how they can be integrated into a React component via the withEffects higher-order component.

Aperture

An aperture controls the streams of data entering Refract. It is a function which observes data sources within your app, passes this data through any necessary logic flows, and outputs a stream of effect values in response.

Handler

A handler is a function which causes side-effects in response to effect values.

Learn Refract

Documentation

Documentation is available at refract.js.org. We aim to provide a helpful and thorough documentation: all documentation files are located on this repo and we welcome any pull request helping us achieve that goal.

Examples

We maintain and will grow over time a set of examples to illustrate the potential of Refract, as well as providing reactive programming examples: refract.js.org/examples.

Examples are illustrative and not the idiomatic way to use Refract. Each example is available for the four reactive libraries we support (RxJS, xstream, Most and Callbag), and we provide links to run the code live on codesandbox.io. All examples are hosted on this repo, and we welcome pull requests helping us maintaining them.

Contributing

Guidelines

We welcome many forms of contribution from anyone who wishes to get involved.

Before getting started, please read through our contributing guidelines and code of conduct.

Links

Logo

The Refract logo is available in the Logo directory.

License

Refract is available under the MIT license.

Discuss

Everyone is welcome to join our discussion channel - #refract on the Reactiflux Discord server.

Talks

Articles

refract's People

Contributors

bh0 avatar isaac-martin avatar joe307bad avatar kyorkston avatar martypenner avatar nimelrian avatar orther avatar osamaqarem avatar seanstrom avatar sormazi avatar thisraptori avatar troch avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

refract's Issues

Add informative error message

A couple of times people have been thrown off by the lack of any informative error message when they try to create an aperture which doesn't return a stream. In that situation, they end up with the following error:

TypeError: sink is undefined

This comes from the sinkObservable being undefined here:

const sinkSubscription: Subscription = subscribeToSink<Effect>(
sinkObservable,
finalHandler(instance.props, instance.context),
errorHandler
? errorHandler(instance.props, instance.context)
: undefined
)

Which is used here:

export const subscribeToSink = <Type>(
sink: Observable<Type>,
next: (val: Type) => void,
error?: (error: any) => void
): Subscription =>
sink.subscribe({
next,
error
})

We should probably throw an informative error if the sink is undefined! Will take a look at this later today unless someone else wants to pick it up first 😛

LICENSE file should be included in npm bundle

The license text is currently not provided with the npm bundle.
In order to allow automation tools like license-webpack-plugin to pick it up, it should be included there.
Also the license states: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
This includes the npm registry.

React logs warnings since 16.8.6 when the config passed to withEffects does not contain a Context property

React 16.8.6 PR: facebook/react/pull/15226

The contextType of the WithEffects component created by withEffects is undefined and thus invalid when either no config object is passed to withEffects or the passed object does not contain a Context property.

public static contextType = config.Context

This causes React to log warnings in the console.

The contextType should only be set if the value in the config object is not undefined

Want to add an introduction project

Hi! 👋

I've already discussed this with @jtmcgrath but I'd like to get everyone involved.

One thing that I really like to do when trying out a new library is to do the introduction (example: https://www.gatsbyjs.org/tutorial/). I'd like to add one for Refract.

The introduction

Since the power of Refract lies in the way it handles side effects it makes sense to include side effects in the tutorial which will be handled by Refract. I thought of having a simulated environment where you develop a dashboard that receives data and interact with iot devices (iot devices being JS modules). This dashboard acts as the smart hub of this environment.

Goals

As of now I have a couple of requirements in the back of my mind;

  • Takes at max 1h.
  • For some parts of the intro, first write how you'd handle the event in the non-refract way and then refactor it to use refract. To give users a feel of how it improves the code.
  • Users should both be inspired and educated about Refract.

Please let me know your thoughts, I've already started a bit on the dashboard environment and I have several questions related to the PR, but first I'd like to hear here what you guys think of it 😄 .

Is this project done for?

Hey guys! I was very excited when I first read about this and the concept is very enticing. I just wanna know if this project is "dead" or has plans to be actively developed any more.

Issue with useRefract

Hello,

I keep getting this error when I update material-ui from version 3.9.3 to 4.0.0.

TypeError: You provided an invalid object where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
    at Object.exports.subscribeTo (modules.js?hash=65a4cf04ccdfebef31e41c84519adbc1f5ea5676:74727)
    at Object.from (modules.js?hash=65a4cf04ccdfebef31e41c84519adbc1f5ea5676:76131)
    at data (modules.js?hash=65a4cf04ccdfebef31e41c84519adbc1f5ea5676:73708)
    at createComponent (modules.js?hash=65a4cf04ccdfebef31e41c84519adbc1f5ea5676:73709)
    at configureHook (modules.js?hash=65a4cf04ccdfebef31e41c84519adbc1f5ea5676:73954)
    at modules.js?hash=65a4cf04ccdfebef31e41c84519adbc1f5ea5676:73986
    at mountMemo (modules.js?hash=65a4cf04ccdfebef31e41c84519adbc1f5ea5676:58670)
    at Object.useMemo (modules.js?hash=65a4cf04ccdfebef31e41c84519adbc1f5ea5676:58879)
    at Object.useMemo (modules.js?hash=65a4cf04ccdfebef31e41c84519adbc1f5ea5676:2641)
    at useRefract (modules.js?hash=65a4cf04ccdfebef31e41c84519adbc1f5ea5676:73985)

Feature: observe all events

Description

Could be a worthwhile addition: add the ability to observe any event passed to pushEvent by calling component.event() (similar to component.observe() without a propName). Came up when talking some stuff through with @mylesj.

Use case

Before

const aperture = () => component =>
	merge(
		component.event('event1').map(value => ['event1', value]),
		component.event('event2').map(value => ['event2', value]),
		component.event('event3').map(value => ['event3', value]),
	).map(toAnalytics)

After

const aperture = () => component =>
	component.event().map(toAnalytics)

Possible Extension

The example raises the question of what data structure should be returned so that both the eventName and the value are exposed. Maybe providing the ability to pass a mapping function would be a good solution?

const aperture = () => component =>
	component.event((event, value) => [event, value]).map(toAnalytics)

const aperture = () => component =>
	component.event((event, value) => ({ event, value })).map(toAnalytics)

[RFC] Supporting hooks

Supporting hooks

I think the Refract React packages could include hooks for users to be able to choose between HoC and Hooks:

  • A useRefract hook which would be similar to withEffects but with minor differences and limitations (to explore).
    • Observing props: not known yet if possible, needs to be explored.
    • Observing mount should be possible (with useLayoutEffect triggered only the first time)
    • Observing unmount would be on internal useEffect hook being unsubscribed
    • Pushing events and from events shouldn't have any limitation
    • Built-in effects (toProps, asProps and JSX) can't be supported, instead a new built-in effect will be introduced for the useRefract hook to "return" values (like useState).
    • No passing of context, instead user will manually pass initialProps and can include context (using useContext before).
  • A useDeclarativeEffect hook factory which would bring a functional alternative to the useEffect hook in React: bringing the global effect handler technique to hooks, but without reactive streams.

How it could look like

useRefract hook would possibly need to be user-land created

const useRefract = createRefractHook(handler, errorHandler)
function MyComponent() {
    const { searchValue, setSearchValue } = useRefract(component => {
        const [searchValue$, setSearchValue] = component.useEvent('search', '')

        return searchValue$.pipe(
            map(searchValue =>
                toComponent({
                    searchValue,
                    setSearchValue
                })
            )
        )
    })

    return <input value={searchValue} onChange={setSearchValue} />
}

Injecting dependencies and props

Users will be able to pass props and context (using useContext before useRefract). Two options:

  • Having useRefract() take a second argument
    const dependencies = { prop1, prop2, context1 }
    const {} = useRefract(dependencies => component => {}, dependencies)
  • Letting users having to create aperture factories and passing arguments, or use closures. In that case the hook API would be useRefract(component => {/*...*/})

toComponent effect creator

const toComponent = (data) => ({
    type: '@@refract/effect/TO_COMPONENT',
    payload: data
})

Initial implementation idea

useRefract would combine useState (used to return state and callbacks to the user component) and useEffect (used to pass component and to subscribe / unsubscribe to the returned stream).

const EmptyContext = React.createContext({})

const createRefractHook = (handler, errorHandler, DependencyContext = EmptyContext) => {
    const useRefract = (apertureFactory, initialProps) => {
        const [data, setData] = useState({})

        const dependencies = useContext(DependencyContext)

        const boundHandler = handler(initialProps, dependencies)
        const bundErrorHandler = errorHandler(initialProps, dependencies)
        const aperture = apertureFactory(initialProps, dependencies)

        useEffect(
            createEffectHook(boundHandler, bundErrorHandler, aperture, setData),
            []
        )

        return data
    }

    return useRefract
}

createEffectHook would be the equivalent of configureComponent.

'componentWillReceiveProps' deprecation warning from WithEffects

Hi,

I've started experimenting with Refract, it looks like a great fit for some things we're trying to do. I noticed the following - using React v16.9.0, from create-react-app, I see the following warning in the browser console:

Warning: componentWillReceiveProps has been renamed, and is not recommended for use. See https://fb.me/react-async-component-lifecycle-hooks for details.

* Move data fetching code or side effects to componentDidUpdate.
* If you're updating state whenever props change, refactor your code to use memoization techniques or move it to static getDerivedStateFromProps. Learn more at: https://fb.me/react-derived-state
* Rename componentWillReceiveProps to UNSAFE_componentWillReceiveProps to suppress this warning in non-strict mode. In React 17.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run `npx react-codemod rename-unsafe-lifecycles` in your project source folder.

Please update the following components: WithEffects

I looked at the source for withEffects.ts, and verified it is using the old componentWillReceiveProps method.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.