Coder Social home page Coder Social logo

rescribet / link-redux Goto Github PK

View Code? Open in Web Editor NEW
35.0 6.0 6.0 4.71 MB

Linked Data Rendering for humans with React

Home Page: https://rescribet.github.io/link-redux-todo/#/

TypeScript 99.72% JavaScript 0.28%
linked-data rdf open-data react link-redux hypermedia

link-redux's People

Contributors

joepio 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

link-redux's Issues

A Redux-like connect function

We can think of link-redux as a tool that (among other things) maps linked data to the props of some React component. In that respect, it is comparable to Redux itself, or more specifically, the connect function of Redux. I suggest adding something exaclty like that: the linkConnect function.

This has a couple of advantages. First of all, many React developers are familiar with the connect wrapper function, which will make link-redux feel familiar. Secondly, it can be used as a wrapper function for existing React components, so developers can use their existing components. And finally, it enables developers to easily combine data from different props in one component (which solves #6).

Here's a suggestion for how it might work in practice:

import React, { PropTypes } from 'react';
import { linkConnect, Property } from 'link-redux';
import { connect } from 'react-redux';

// The registerComponent object is passed as the first argument to linkConnect.
const registerComponent = {
  // The 'topology' prop is used to determine in which context this component should be used.
  topology: 'card',
  // Set the predicate(s) that this component should render.
  // You can pass a string or an array of strings.
  type: ['schema:Thing'],
};

// This tells linkConnect to map data to the defined property names.
const mapLinkProps = {
  // Get the schema:name property and pass it to the component as `title`.
  title: 'https://schema.org/name',
  someArray: {
    // Use an object to configure the data that you need
    // When there are multiple instances of a property (a list, for example), you can
    // tell Link to pass the various items as an array, instead of just the first value
    list: true,
    // The predicate is required. You can use a shorthand, if you have configured it
    predicate: 'schema:list',
    // Specify the language version.
    lang: ['en', 'nl'],
  },
  // Use an array to show which types this comonent should render.
  // The first in the array have the highest priority
  text: ['schema:text, dcterms:description'],
};

const propTypes = {
  // This is passed by the linkConnect function. It contains various useful properties
  linkProps: PropTypes.object.isRequired,
  text: PropTypes.string.isRequired,
  title: PropTypes.string.isRequired,
  someArray: PropTypes.array.isRequired,
};

const MyComponent = ({
  linkProps,
  title,
  text,
  someArray,
}) => {
  // If you want to combine data to calculate something new,
  // do that in your component instead of your selector.
  const titleText = () => title + text;

  return (
    <div>
      {title}
      {text}
      {titleText}
      {someArray.map(item => <span>{item}</span>)}
      {/* You can still use the regular Link-Redux properties. */}
      <Property
        topology="row"
        object={linkProps.iri}
        label="schema:position"
      />
    </div>
  );
};


MyComponent.propTypes = propTypes;

// If you want to access your redux store, simply wrap the connect function in linkConnect
const reduxConnect = connect(
  (state, props) => ({
    someLocalState: someSelector(state, props.title),
  }),
  dispatch => ({
    onAction: () => dispatch(someAction()),
  })
);

// This is where the magic happens.
export default linkConnect(
  registerComponent,
  mapLinkProps
)(reduxConnect(MyComponent));

Making this linkConnect function seems difficult, since it needs to be able to (efficiently) determine which components need new props and which do not. This is the reason why Redux is so performant.

Perhaps it's a good idea to extend the existing redux connect function.

Use the values of multiple props in a single component

Some view components are dependent on the values of various properties. I suggest adding a wrapper function similar to the redux connect function (connectLink(), perhaps?), where a set of properties for a certain resources could be fetched which are in turn passed to the wrapped component.

A possible downside to this suggestion is that it might stimulate lazy development. Properties lose relevant metadata and awareness of what they represent. They are no longer responsible for their own render function.

However, it would greatly improve the workflow for Link developers and it enables more complex composite components.

Component for listing other props

Currently, the props that are rendered need to be specified. I feel like this tool would be more powerful if it could also render all the properties of a resource that do not yet have a know view.

In order for this to be possible, a couple of things need to change:

  • There should be a generic property view. I suggest that the default component prints the predicate and the object. If some rdf:label of the predicate is known, render that instead to increase readability. Users could overwrite this view by registering their own DefaultProp component.
  • The state should know which props are being rendered to prevent duplicate renders. Use an ignore prop to le the component know which properties are rendered somewhere else to prevent duplicate renders.

PropertyRender ignores property

The property renderer receives the prop matching the label.

<Property label={[argu.trashActivity, argu.trashedAt]} />

TrashActivity.property = argu.trashActivity;

The linkedProp passed to TrashActivity is trashedAt

Pass property param via props

We currently pass the property values via the getLinkedObjectProperty method, but passing it via the props seems more efficient. Since some assumptions are made by getLinkedObjectProperty, passing its raw version would be desirable as well.

Easy way to convert RDF Lists to Arrays

A developer playing with RDF will, sooner or later, realize that RDF does not support Arrays. RDF Lists have their merits, but they are a struggle to deal with as a front-ender.

If I want to render an RDF list, I'd need to register a view like this one:

import React from "react";
import { register, Property, useLinkRenderContext } from "link-redux";

import { NS } from "../../LRS";
import allTopologies from "../Topologies/allTopologies";

const RDFList = () => {
  const { subject } = useLinkRenderContext()

  // Without this, it renders the nil - which often looks weird
  // Also requires this in the ontology: 
  //new Statement(NS.rdf("nil"), NS.rdf("type"), NS.rdf("List")),

  if (subject.value === NS.rdf("nil").value) {
    return null;
  }

  return (
    <React.Fragment>
      <Property label={NS.rdf("first")}/>
      <Property label={NS.rdf("rest")}/>
    </React.Fragment>
  );
};

RDFList.type = NS.rdf("List");
RDFList.topology = allTopologies;

export default register(RDFList);

Which has a couple of problems:

  • It creates a huge nested set of components, instead of a linear list
  • I can't manipulate the array in a way that empowers me as a front-ender. No map, filter, push.

I feel like link-lib, link-redux or maybe rdflib should provide a sensible way to help with RDF Lists.

Perhaps the mapDataToProps function could default to return RDF lists as arrays? Perhaps the Collection model of rdflib.js can be used?

Add open source license

The LGPL license is mentioned in the package.json, but not in the readme or in the license file.

Some thoughts on RDFa

Since link-redux effectively converts RDF to HTML, the RDFa spec seems to be relevant. There are various ways link-redux could add RDFa properties to generated HTML.

  1. Wrap all resources and properties in div's that include all the relevant attributes, or add relevant RDFa attributes to elements.
  2. Add a helper function that developers could use to add to their own property / resource component. They could call this function in the top-level element, which adds the RDFa attributes.

What are your thoughts on this? Is adding RDFa attributes valuable?

Auto-render IRI properties without views

The code leaves a lot of boilerplate to connect IRI properties to their respective type renderers. So it seems save to assume that we can render a LinkedObjectContainer whenever the property is a proper link (@id) and no property renderer is found

Use the React Hooks API

The mapDataToProps provides a useful and easy way to map specific RDF properties to local JS representations. However, this requires that a component is registered to LRS, and it is wrapped in a register call.

The recently added React Hooks API introdcuces a clean way to get dynamic data in React components. I think this might be a useful and intuitive API to use in this project.

A custom Link-Redux hook could look something like this:

function BlogPreview(props) {
  const title = useProp(props.subject, "https://shema.org/title");
  const intro = useProp(props.subject, "https://example.com/ns/articleIntro");

  return (
    <article>
      <h1>{title}</h1>
      <p>{intro}</p>
    </article>
  );
}

The returned type depends on the object value of the RDF triple (IRI, string, number).
Perhaps we could store the RDF triple

Other hooks might be useful as well:

  • Getting the (first) parent subject useSubject()
  • Getting the topology useTopology()

What do you think, @fletcher91? Does this fit in this project, or should it be a different library altogether?

Suggestion: Use `@context`-like objects for mapping inside front-end code

When developing apps using link-redux, I tend to write a lot of explicit references to RDFS Classe URIs and RDFS Property URIs. These are error-prone and cumbersome to type.

Although LRS.namespaces helps a little with mapping ontologies, it is not optimal and unfamiliar to devs:

// In your LRS
LRS.namespaces.schema = Namespace("http://schema.org/");

// Inside your component
import { NS } from "../LRS"
<Property label={NS.schema("location")} />

I suggest using JSON-LD @context and enable ORM-like syntax, similar to LDFlex. Perhaps it's easier to just use .js files, so IDE's could do things like autocomplete / auto-import.

// In your context.js
export default {
    schema: "http://schema.org",
    location: { "@id": "schema:location", "@type": "@id"}
}

// In your component
import { location } from "../context"
<Property label={location} />

Change default behavior when mapDataToProps property is missing

Currently, when a property is requested in mapDataToProps, but it is missing in the data, the result is that the component does not render and no error is emitted.

This can make debugging quite hard. When I can't see a view, there can be many possible causes:

  • Is my view registered correctly?
  • Does the type of the data matches my View?
  • Do I have any typo's in my View? (can happen in mapDataToProps, in the props of the component, in the proptypes / interface)
  • Is some prop missing in the data?

We might be able to rule out the last two by setting forceRender to true, and checking the logs:

MyComponent.linkOpts = {
  forceRender: true,
};

But I feel like this should not be necessary.

Possible default behaviors for when a mapDataToProps prop is missing in the data:

  • Render an Error component / throw error (in console)
  • Render another View (fallback / Thing view) (play maybe throw error)

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.