rescribet / link-redux Goto Github PK
View Code? Open in Web Editor NEWLinked Data Rendering for humans with React
Home Page: https://rescribet.github.io/link-redux-todo/#/
Linked Data Rendering for humans with React
Home Page: https://rescribet.github.io/link-redux-todo/#/
Since it's such a fundamental, important, often-used component, I feel it should have a shorter name. How about Resource
?
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.
Relates to ontola/openbesluitvorming#48
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.
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:
rdf:label
of the predicate is known, render that instead to increase readability. Users could overwrite this view by registering their own DefaultProp component.ignore
prop to le the component know which properties are rendered somewhere else to prevent duplicate renders.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
The Property(Base)
component assumes access to the context objects and crashes with non-descriptive errors, which can be improved by checking beforehand.
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.
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:
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?
The LGPL license is mentioned in the package.json, but not in the readme or in the license file.
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.
div
's that include all the relevant attributes, or add relevant RDFa attributes to elements.What are your thoughts on this? Is adding RDFa attributes valuable?
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
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:
useSubject()
useTopology()
What do you think, @fletcher91? Does this fit in this project, or should it be a different library altogether?
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} />
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:
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:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.