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

jinaga-react

v5.0.1

Published

React binding helpers for Jinaga

Downloads

27

Readme

Jinaga React

Binding helpers for managing React component state based on Jinaga watches.

npm install --save jinaga react react-dom jinaga-react

Full documentation can be found at https://jinaga.com/documents/jinaga-react/.

Composition

User interfaces in React are composed from components. The Jinaga helper library for React provides ways to connect components to Jinaga template functions to create dynamic user interfaces. It breaks the problem into three layers:

  • Specifications
  • Mappings
  • Containers

Mappings can further be used in collections to build structures of any depth.

Specifications

Start by specifying a set of properties to be injected into a React component.

const messageSpec = specificationFor(Message, {
    text: field(m => m.text),
    sender: property(j.for(Message.sender).then(UserName.forUser), n => n.value, "<sender>")
});

This specification defines two props. The text prop will be given the value of the text field of the Message fact. The sender prop will use the result of the template functions Message.sender and UserName.forUser. This sets up a query that will take the sender of the message, and then watch for their user name.

By default, the property will have the value "<sender>", which is just there so that something gets displayed. That value will be used only if the sender has no name. In other words, if the UserName.forUser template matches no facts.

You can get the results of a mapping with the hook useResult. Pass the Jinaga object, the starting fact, and the specification. Like all hooks, this can only be used within a render function -- typically in a function component.

const messageView = ({ message }) => {
    const result = useResult(j, message, messageSpec);

    if (result === null) {
        return <p>Loading</p>;
    }
    else {
        const { text, sender } = message;

        return (
            <>
                <p className="message-text">{text}</p>
                <p className="message-sender">{sender}</p>
            </>
        );
    }
}

The hook returns null while the results are loading asynchronously, or if the starting fact is null.

Mappings

Once you have a specification, you can map it to a React component. Do this by calling the mapProps function with the specification, and then the to function with the component. Function components or class components are accepted. If the component takes additional properties, you can use the generic argument on to to declare them (TypeScript only).

interface MessageProps {
    onReply(): void;
}

const messageMapping = mapProps(messageSpec).to<MessageProps>(({ text, sender, onReply }) => (
    <>
        <p className="message-text">{text}</p>
        <p className="message-sender">{sender}</p>
        <button onClick={onReply}>Reply</button>
    </>
));

Or if you prefer a class component rather than a function component, pass the constructor.

class MessagePresenter extends React.Component {
    constructor(props) {
        super(props);
    }

    render() {
        return (
            <>
                <p className="message-text">{this.props.text}</p>
                <p className="message-sender">{this.props.sender}</p>
                <button onClick={this.props.onReply}>Reply</button>
            </>
        );
    }
}

const messageMapping = mapProps(messageSpec).to(MessagePresenter);

Containers

Now that you've mapped the specified properties into a component, you can wrap that component in a container. Define a container component with the jiangaContainer function. Pass in the Jinaga instance (typically called j) and the mapping.

const MessageView = jinagaContainer(j, messageMapping);

You can now use this container component as a regular React component. It has a prop called fact that takes the starting point of the graph. It also takes any unbound props that were added in the call to mapProps.to.

const message = new Message("Twas Brillig", user, new Channel("General"), new Date());

function onReply() {
    // ...
}

ReactDOM.render(
    <MessageView fact={message} onReply={onReply} />,
    document.getElementById("message-host"));

Collections

Of course, it doesn't make much sense to have a page that displays just one message. You want a list of messages in a channel. You can compose mappings into other specifications using the collection function.

const channelSpec = specificationFor(Channel, {
    identifier: field(c => c.identifier),
    Messages: collection(j.for(Message.inChannel), messageMapping, descending(m => m.sentAt))
});

I gave the Messages prop a capitalized name. Want to know why? Because that lets me use it as a component! Supply any unbound parameters.

const channelMapping = channelSpec(( { identifier, Messages }) => (
    <>
        <h1>{identifier}</h1>
        <Messages onReply={onReply} />
    </>
));

The collection component will render all of the results of the template function using the child mapping, and in the specified order.

There are loads of ways to compose specifications and mappings. The field specification functions include:

| Function | Purpose | Example | | -- | -- | -- | | field | Pluck one field from the fact. Also used to fetch the fact itself. | text: field(m => m.text) | | projection | Map a single child component. | UserView: projection(userViewMapping) | | collection | Map a collection of child components. | Messages: collection(j.for(messagesInChannel), messageMapping) | | property | Match a single value of a mutable property. | name: property(j.for(nameOfUser), n => n.value, "<user>") | | mutable | Match all candidate values of a mutable property. | name: mutable(j.for(nameOfUser), userNames => userNames.map(n => n.value).join(", ")) | | array | Construct an array of child objects. | messages: array(j.for(messagesInChannel), { text: field(m => m.text) }) |