npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

fetch-tree

v3.5.0

Published

The goal is to allow you to define data requirements with your components. Those data requirements can then be composed so that a top level component can load all of the required data for it and its children.

Downloads

5

Readme

fetch-tree

The goal is to allow you to define data requirements with your components. Those data requirements can then be composed so that a top level component can load all of the required data for it and its children.

You define data requirements using a set of functions that generate nodes. Nodes are plain objects with as few functions as I could get away with. If you log the tree you built nodes will always have a TYPE property and many have a children property.

This project was inspired by redux-loader.

Getting Started

FetchTree requires that you are using redux, react-redux, and redux-thunk.

npm install --save fetch-tree

Mix the reducer into your root reducer under the key fetchTree

import { reducer as fetchTree }  from 'fetch-tree'

export default combineReducers({
    fetchTree,
})

The default export is a function for generating a higher order component.

import fetchTree from 'fetch-tree'

export const resources = /* */

export default fetchTree({
    resources,
    component: MyComponent,
    // busy is optional. If you don't provide a component it will just render
    // `null` in place of your component while it loads.
    busy: (props) => (<div>Loading...</div>),
})

Resource nodes

The most important node types are:

  1. selector(selectFunction) - Wraps an ordinary selector to pull data from your store
  2. loader({ id, action, selector, lazy = false }) - Allows you to manage how and where actions are fetched.
  3. entity({ id, apiFunction }) - You provide a function to call to fetch data and store it
  • entity is actually a shortcut for loader where fetch-tree provides the action and selector

Everything else exists to organize these three nodes.

selector(selectorFunction(state, ...params))

Selectors are the easiest type to use. Other nodes that accept a child node will allow you to pass a selector function and it will just convert it.

...params are optional and will be provided by the depends() node.

const someSelector = (state) => state.thing
const resources = group({
    someProp: selector(someSelector)

    // automatically converted by group():
    otherProp: someSelector
})

loader({ id, action, selector, lazy = false })

fetch-tree needs an ID to keep track of pending and completed requests. We do that with an id(...params) function. If you have a resource that loads individual todos by ID you might use

id: (todoId) => `todo-${todoId}`

action must be a thunk style action that returns a promise that resolves when the data has been written to the store.

selector must select whatever data action wrote to the store.

loader({
    id: (...params) => `resource-type-`
    action: (...params) => (dispatch) => return new Promise((resolve, reject) => {

    })
    selector: (state, ...params) =>
})

entity({ id, apiFunction })

entity is a special version of loader that replaces action and selector with a single apiFunction. apiFunction needs to return a Promise for whatever data you want to store under that key.

group({ [propName]: node, [propName]: node })

group converts each node to a value. The most common use for this is as resources for a component. Every key that where the node is ready becomes a prop that will be passed down to your component.

const resources = group({
    someProp: /* node definition */
})

It's best to define groups with an object literal, because it's going to process each node in order.

As group processes, each node becomes known as a resource that you may depend on for other nodes.

group([ node, node, node ])

When group is given an array, it will return an array with the values of each node.

group(factory)

When group is given a factory function, it allows you to generate a group while the FetchTree is being processed. factory is given any depends parameters (much like selector and loader), which is useful for creating a group that depends on component properties, or loaded values of other resources.

const todoLoader = loader(/* assume it needs an ID */)

const resources = group({
    ids: virtual(fromProps('ids')),
    allTodos: depends(
        [ 'ids' ],
        group((ids) => ids.map(
            id => depends([ () => id ], todoLoader)
        ))
    ),
})

depends(dependencies, child)

This node populates ...params for its child node. dependencies is an array of nodes and dot-separated paths referring to other resources in this group.

const resources = group({
    resourceA: selectA,
    resourceB: selectB,

    depends(
        [
            'resourceA',
            'resourceB.name',
            (state) => state.someProperty
            () => 'some hardcoded value'
        ],
        selector((state, resourceA, resourceBName, componentProperty, hardcodedValue) => {
        })
    )
})

virtual(child)

virtual allows you to define resources in a group that don't get passed along as props in your component.

const resources = group({
    notAProp: virtual(selector(someSelector)),

    someProp: depends(
        ['notAProp'],
        selector((state, notAPropValue) => {

        })
    )
})

fromProps(propPath)

fromProps usually (see withProps) references the props given to your component connected with FetchTree. propPath is dot-separated path.

const resources = group({
    todoId: virtual(fromProps('category.todoId')),
    todos: depends(
        [ 'todoId' ],
        todoLoader
    )
})

withProps(props, node)

withProps sets the props used by fromProps. This is usually set for you using your connected component's props by the FetchTree higher order component but can be useful to set yourself. For example, withProps can be used to "preload" resources, that is, to cause some parent component to be considered "loading" until a cache has been prepared for the children it renders.


const todoResource = group({
    id: virtual(fromProps('id')),
    todo: depends( ['id'], todoLoader ),
})

const todoListResource = group({
    ids: virtual(fromProps('ids'))
    todos: depends(
        ['ids'],
        group((ids) => {
            return ids.map(
                id => withProps({ id }, childTree)
            )
        })
    ),
})

debug(child)

You can wrap nodes in debug to log that section of the tree, and to log what's happening as it gets processed.

group.debug(children) is a shortcut for debug(group(children)). This allows you to quickly add and remove debugging without having to deal with parens that might be many lines away.

Open Source

This library is written for and maintained by Unizin. We offer it without guarantees because it may be useful to your projects. All proposed contributions to this repository are reviewed by Unizin.

Goals

  • manage all data for each component
  • selecting data is as easy as loading data
  • requirement trees can be composed
  • debuggable
  • Loading screen shows when the data hasn't arrived
  • revisiting a component will show cached data, but will also fetch fresh data in the background