{"id":32991303,"url":"https://github.com/rescribet/link-redux","last_synced_at":"2026-01-12T10:31:20.833Z","repository":{"id":39862078,"uuid":"76942302","full_name":"rescribet/link-redux","owner":"rescribet","description":"Linked Data Rendering for humans with React","archived":false,"fork":false,"pushed_at":"2022-09-09T14:03:42.000Z","size":4821,"stargazers_count":36,"open_issues_count":5,"forks_count":6,"subscribers_count":6,"default_branch":"master","last_synced_at":"2026-01-07T03:56:21.152Z","etag":null,"topics":["hypermedia","link-redux","linked-data","open-data","rdf","react"],"latest_commit_sha":null,"homepage":"https://rescribet.github.io/link-redux-todo/#/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rescribet.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-12-20T09:24:02.000Z","updated_at":"2024-11-28T16:33:05.000Z","dependencies_parsed_at":"2023-01-17T16:45:50.462Z","dependency_job_id":null,"html_url":"https://github.com/rescribet/link-redux","commit_stats":null,"previous_names":["fletcher91/link-redux","fletcher91/link-react"],"tags_count":49,"template":false,"template_full_name":null,"purl":"pkg:github/rescribet/link-redux","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rescribet%2Flink-redux","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rescribet%2Flink-redux/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rescribet%2Flink-redux/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rescribet%2Flink-redux/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rescribet","download_url":"https://codeload.github.com/rescribet/link-redux/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rescribet%2Flink-redux/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28338380,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-12T06:09:07.588Z","status":"ssl_error","status_checked_at":"2026-01-12T06:05:18.301Z","response_time":98,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hypermedia","link-redux","linked-data","open-data","rdf","react"],"created_at":"2025-11-13T09:00:33.300Z","updated_at":"2026-01-12T10:31:20.825Z","avatar_url":"https://github.com/rescribet.png","language":"TypeScript","funding_links":[],"categories":["Programming","TypeScript"],"sub_categories":["JavaScript"],"readme":"# Link-Redux\n*A Link to the Web*\n\n[![CircleCI](https://img.shields.io/circleci/build/gh/rescribet/link-redux)](https://circleci.com/gh/rescribet/link-redux)\n[![Read the Docs](https://img.shields.io/readthedocs/pip.svg)](https://rescribet.github.io/link-redux/)\n![Code Climate maintainability](https://img.shields.io/codeclimate/maintainability/rescribet/link-redux)\n![Code Climate coverage](https://img.shields.io/codeclimate/coverage/rescribet/link-redux)\n![npm version](https://img.shields.io/npm/v/link-lib.svg)\n![npm type definitions](https://img.shields.io/npm/types/link-redux.svg)\n[![License: LGPL v3](https://img.shields.io/badge/License-LGPL%20v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0)\n\nLink-Redux (or Link) helps you render [Linked Data](https://ontola.io/what-is-linked-data/) (RDF) in React.\n\n## [Reference](https://github.com/rescribet/link-redux/wiki): how to use Link (cheatsheet)\n## [Docs](https://rescribet.github.io/link-redux/): functions, classes, methods, types\n\n## Features\n- RDF data fetching \u0026 parsing\n- View management (error \u0026 loading handling, smart lookups, adapts to data)\n- Rule-based inference engine to determine which View is suitable\n- Actions / Data manipulation (in a flux pattern, like redux, but with [server-side actions](https://github.com/rescribet/link-lib/wiki/Hypermedia-API))\n\n## When you'd might want to use Link\n- Embedding Linked Data into your existing app\n- [SOLID](https://github.com/solid/solid) apps\n- Multi-domain linked-data browsers\n- Full Rest-in-Rest hypermedia systems\n\n## Rendering a Person\nSay we want to render this bit of linked data:\n\n```turtle\n\u003chttps://example.com/somePerson\u003e a \u003chttp://xmlns.com/foaf/0.1/Person\u003e .\n\u003chttps://example.com/somePerson\u003e \u003chttp://schema.org/name\u003e \"Jane Doe\" .\n\u003chttps://example.com/somePerson\u003e \u003chttp://schema.org/birthPlace\u003e \u003chttp://dbpedia.org/page/London\u003e .\n```\n\nIn our app we create a `Resource` to render our subject:\n\n```javascript\nimport Resource from \"link-redux\"\n\n\u003cApp\u003e\n  \u003cResource subject=\"https://example.com/somePerson\" /\u003e\n\u003c/App\u003e\n```\n\nNow, Link will try to fetch our subject and will show a Loading component.\nIf it fails, an Error component will render.\nIf it succeess, Link will look for the best possible registered view.\nCurrenlty, no views are registered, so we'll need to register one:\n\n```javascript\n// We don't have to type out the URLs for the ontologies\nimport * as schema from \"@ontologies/schema\"\nimport { Person } from \"@ontologies/foaf\"\nimport { register } from \"link-redux\"\n\nconst MyPersonView = ({ birthPlace, name }) =\u003e (\n  \u003cdiv\u003e\n    \u003cp\u003e{name.value}\u003c/p\u003e\n    \u003cResource subject={birthPlace} /\u003e\n  \u003c/div\u003e\n)\n\n// Here we tell Link that this component is suited for rendering a foaf:Person\nMyPersonView.type = Person // =\u003e http://xmlns.com/foaf/0.1/Person\n// mapDataToProps makes the property values available in the component\nMyPersonView.mapDataToProps = {\n   birthPlace: schema.birthPlace,\n   name: schema.name\n}\n// The component is registered as a View in Link\nregister(MyPersonView)\n```\n\nThat's it! There's a lot more you can do with Link, though.\nYou can create custom Property renderers, dispatch actions, and specify which views should render where using Topologies.\n\n## Usage\n`npm install link-lib link-redux rdflib` V `yarn add link-lib link-redux rdflib`\n\n### Example apps \u0026 related repositories\n- See the [TODO app](https://rescribet.github.io/link-redux-todo/#/) for a live example and [the source](https://github.com/rescribet/link-redux-todo) for the implementation. Mind that it isn't connected to a back-end, so it's only a demo for the view rendering mechanism.\n- [ori-search](https://github.com/ontola/ori-search/) uses Link to render Dutch Government meetings.\n- [mash](https://github.com/ontola/mash) is a databrowser, compatible with SOLID\n\n## Architecture\nFirst off, only very basic knowledge of the underlying technologies (RDF and REST) is required to build\napplications with Link. It'll provide you with the benefits of hypermedia, while abstracting some of\nthe difficulties.\n\nLink was designed to work with RDF and REST (including [HATEOAS](https://en.wikipedia.org/wiki/HATEOAS))\nprinciples, therefore it has a different architecture than the general react app. This is in large part\ndue to the [open world assumption](http://www.dataversity.net/introduction-to-open-world-assumption-vs-closed-world-assumption/)\ncausing us to handle uncertainty about the shape of the data that we're going to get from the server.\n\nThough it's easy to just render a table and call it a day, to create rich, interactive applications,\nwe have to make _some assumptions_ about the data, but when building a Link application, you should\nstill be able to render data from a variety of data sources while being able to tune UX for the most\nimportant use-cases.\n\nDue to those constraints, part of the application logic resides in the views themselves. There are\ndifferent types of views to handle the different use cases which the constraints impose.\n\n### Topology providers\nThink of these as UI API definitions. These should set the UI limits of a component, and the general\nprops that can be passed to them.\n\nTo identify the different topologies in your app. Take a look at your design and identify the places\nwhere data is rendered in different formats. Well known UI-library components (article, button,\nlist item, table cell) should come to mind, as well as components specced in wire frames (page, menu,\nsidebar, hover popup, etc). Over time, as your app develops, these can become quite plentiful. If\nthere are too many, consider redesigning some parts to simplify its core structure.\n\nLet's take the last one, hover bubble. A user hovers over a link and a bubble should appear with a\npreview.\n\n```JSX Harmony\nimport { TopologyProvider } from 'link-redux';\n\n/**\n * Define an IRI for the topology. It's handy to use the app namespace since the topology is probably\n * app specific. However, when building a UI library, an iri of that library would be more useful\n * since the layout requirements (css) of each component would be fully documented.\n */\nexport const popupTopology = app('topologies/popup');\n\n/**\n* This component is to set the topology. In the real world, it'd be used in a component which\n* manages the portal to render the popup. Documentation can be used to define which props can be\n* expected by components rendered under this topology. In this case a `close` method for closing the\n* popup could be useful.\n*/\nclass Popup extends TopologyProvider {\n  constructor(props) {\n    super(props);\n\n    // Set the topology for all children to the newly defined.\n    this.topology = popupTopology;\n    // It's possible to set a class name on the wrapping component.\n    this.className = 'Bubble--container';\n  }\n\n  // The render method is completely optional\n  render() {\n    // Since a popup always renders a new resource, we'll override rendering to set a new\n    // microdata context.\n\n    // This property also works out of the box, defaulting to a div.\n    const elem = this.elementType;\n\n    return this.wrap((subject) =\u003e {\n       \u003celem className={this.className} resource={subject \u0026\u0026 subject.value}\u003e\n         {this.props.children}\n       \u003c/elem\u003e\n    });\n  }\n}\n```\n\nMounting this component in the tree will cause any child to be resolved in the new topology. Be sure\nto register a component with it, otherwise no view can be found an an error will occur.\nappropriate format.\n\n### Resource renderers\nTo tell link to fetch and render a resource, just mount a `Resource` anywhere\nin your react app. Because the component will try to render every IRI passed, a good place for the\ninitial mount would be in the router, especially the fallback route. If choosing to use the Resource\nas a router, make sure all your resources are resolvable (including the fragment), otherwise a 404\nwill occur.\n\n```JSX Harmony\nimport rdf from '@ontologies/core'\nimport { namedNodeByIRI, Resource } from 'link-redux';\n\nclass OurTeamPage extends React.PureComponent {\n  /* details omitted */\n\n  render() {\n    /**\n    * We use `Grid` here to communicate to the resolved views what their environment is, based on\n    * the registered views for their class (probably \u003cschema:Person\u003e) they'll probably render as a\n    * card with their profile picture, name and a small description.\n    *\n    * The {Resource} component expects `subject` to be an instance of rdflib/NamedNode. Obtain one by either:\n    * - Using a namespace, e.g. app('person/alice')\n    * - Using namedNodeByIRI to get one from a string\n    *\n    * NOTE: Using new NamedNode() from rdflib WILL NOT WORK(!) due to implementation details.\n    */\n    return (\n      \u003cdiv\u003e\n        \u003ch1\u003eOur team:\u003c/h1\u003e\n        \u003cGrid\u003e\n            \u003cResource subject={app('person/alice')} /\u003e\n            \u003cResource subject={rdf.namedNode('https://example.com/person/bob')} /\u003e\n        \u003c/Grid\u003e\n      \u003c/div\u003e\n    );\n  }\n}\n```\n\nSo all the underlying logic of managing API calls, data fetching, views selecting etc will be\nhandled by the `Resource` component. To show a loading indicator (or an error) for\ngrid mounted resources while alice and bob are loading, see [Loading and error handling](https://github.com/rescribet/link-redux#loading-and-error-handling).\n\n### Property renderers\nProperty renderers are akin to resource renderers, but they work with one or more properties of a\nresource, rather than an entire resource. This can be useful in a diverse range of use cases;\n* When wanting to separate the main resource implementation from property rendering to keep individual components clean.\n* When the range (value) of a property isn't known yet - e.g. it could be a number or an entire nested resource.\n* When a range of similar properties is appropriate in a location, but only one should be displayed - e.g. \u003cschema:name\u003e, \u003crdfs:label\u003e, \u003cfoaf:name\u003e\n* When combining multiple properties into a single view - e.g. geo-coordinates (latd, latm, latns, longd, longew, longm)\n\nWhen a property has no view registered, and the resolved value is a NamedNode (a link/href), it'll\nautomatically mount a Resource so the nested resource is rendered.\n\n```JSX Harmony\nimport * as foaf from '@ontologies/foaf';\nimport * as schema from '@ontologies/schema';\nimport { Property } from 'link-redux';\n\nconst nameTypes = [schema.name, foaf.name];\n\nclass PersonGrid extends React.PureComponent {\n  /* details omitted */\n\n  render() {\n    // The label defines which properties we like to render.\n    return (\n      \u003cdiv\u003e\n        \u003cProperty label={[schema.name, foaf.name]} /\u003e\n      \u003c/div\u003e\n    )\n  }\n}\n\n\nclass PersonName extends React.PureComponent {\n  /* details omitted */\n\n  render() {\n    // The value of the property is passed to property renderers as `linkedProp`, note that it is a\n    // RDFLib Literal object, to get the string representation call `linkedProp.value` e.g. 'Alice'\n    return (\n      \u003cp\u003e{this.props.linkedProp.value}\u003c/p\u003e\n    );\n  }\n}\n```\n\n#### Actions and the Middleware\nSo far we have seen how to render the data, but applications wouldn't be complete without\ninteractivity.\n\nTraditionally web applications differentiate between actions which modify client state (e.g. showing\na popup) and those which modify server state (e.g. doing a POST requests). Server actions can be\ncharacterised by the URL to send the request to, together with some optional request body. The\nclient side is more diverse (i.e. using events, passing callbacks, redux's action system, etc.) but\nlink re-uses the server-side interface for modifying client state as well, all actions written with\nthe tools link provides can be executed by providing the system with an URL and an optional body.\n\nConstraining client actions to be able to be described via URLs (and an optional body) might seem\nsomewhat cumbersome, but it enables some very interesting behaviour. It allows rendering both server\ndata and client state with the same tools, as well as allowing the the logic which is needed to\nfulfill the action between client and server without breaking the system.\n\nTo execute an action, just call `lrs.exec(iri: NamedNode, payload: any)`. By default link will search\nthe store for the resource (`iri`) or fetch it if it doesn't exists, if the resource is some\n[schema:Action](https://schema.org/Action) it will try and fulfill the request, updating the store\nwith the changes the server requested (see the the\n[hypermedia API documentation](https://github.com/rescribet/link-lib/wiki/Hypermedia-API) for more\ninfo).\n\nThe exec method is a middleware function with a handler for doing http requests (via schema:Action\nresources) pre-installed. You can add your own handlers to the stack to implement behaviour for other\nURLs or to override the default behaviour.\n\nFurthermore, if the server responds with the `Exec-Action` header, the server can execute an action\nin the front-end (e.g. trigger some popup to perform a required action). This is what sets it apart\nfrom just using redux for state management, since it allows the front-end to execute actions on far\nmore data than a client device could calculate, resulting in more powerful experiences without\nduplicating logic as well.\n\nThe following example implements the state management for a popup, the `processDelta` calls are used\nto change the state. The syntax is somewhat verbose for middleware this size, but the overhead is\nacceptable for real-world implementations, since you shouldn't need a lot of these methods (most\nactions should be handled automatically with the hypermedia API).\n\n```JSX Harmony\n// src/ontology/popup.js\nimport { createNS } from '@ontologies/core';\n\n// Define a new namespace for our popup system\nconst ns = createNS(\"https://mySite.com/ns/popup#\");\n\n// Define some classes, properties, and individuals within our ontology.\nexport default {\n  ns,\n\n  /* Classes */\n  PopupManager: ns('PopupManager'),\n\n  /* Properties */\n  resource: ns('resource'),\n  open: ns('open'),\n  close: ns('close'),\n\n  /* Individuals */\n  manager: ns('manager'),\n}\n```\n\n```JSX Harmony\n// src/middleware/popup.js\nimport rdf, { createNS } from '@ontologies/core';\nimport * as rdfx from '@ontologies/rdf';\nimport { replace } from '@rdfdev/delta';  // See https://github.com/argu-co/linked-delta for the exact meaning\nimport popup from '../ontology/popup';\n\nconst PopupMiddleware = (store) =\u003e {\n  // Make the namespace available throughout the app\n  // Set up the state in the store, e.g. which resource to render and if it should be shown\n  store.processDelta([\n    replace(\n      popup.manager,\n      rdfx.type,\n      popup.PopupManager,\n    ),\n    replace(\n      popup.manager,\n      popup.resource,\n      popup.null,\n    ),\n    replace(\n      popup.manager,\n      popup.open,\n      rdf.literal(false),\n    ),\n  ]);\n\n  /**\n  * A helper function which defines how the state should be modified.\n  */\n  const showPopup = (iri) =\u003e store.processDelta([\n      replace(\n        popup.manager,\n        popup.resource,\n        rdf.namedNode(iri),\n      ),\n      replace(\n        popup.manager,\n        popup.open,\n        rdf.literal(true),\n      ),\n    ]);\n\n  const hidePopup = () =\u003e store.processDelta([\n      replace(\n        popup.manager,\n        popup.open,\n        rdf.literal(false),\n      ),\n    ]);\n\n  //  It's useful to always return a promise (e.g. use `async` if you have ESnext), so the caller\n  //  can do stuff after the middleware is done.\n  return next =\u003e (iri, opts) =\u003e {\n    // Skip non-popup actions\n    if (!iri.value.startsWith(popup().value)) {\n      return next(iri, opts);\n    }\n\n    // Display a popup\n    if (iri.value.startsWith(iri.value.startsWith(popup.show.value))) {\n      const resource = new URL(iri.value).searchParams.get('resource');\n\n      return showPopup(resource);\n    }\n\n    // Its a popup action, but not a display, so default to hiding\n    return hidePopup();\n  };\n};\n```\n\nAfter setting up, you can wrap your favorite popup library with the `link` method to retrieve the\nstate from the store;\n\n```JSX Harmony\n// Mount `\u003cResource subject={popup.manager} /\u003e` somewhere in your app\n\nimport { Resource } from 'link-redux';\nimport popup from '../ontology/popup';\n\nclass PopupManager extends React.PureComponent {\n  static type = popup.PopupManager;\n\n  static mapDataToProps = [popup.resource, popup.open];\n\n  render() {\n    const { open, resource } = this.props;\n\n    if (open.value === 'false') {\n      return null;\n    }\n\n    // The \u003cPopup /\u003e wrapper here is a TopologyProvider. If we were lazy, we could omit this and set\n    //  the `topology` prop on Resource, but consistency is maintainability.\n    return (\n      \u003cPopupLibrary\u003e\n        \u003cPopup\u003e\n          \u003cResource\n            close={this.props.lrs.exec(popup.close)}\n            subject={resource}\n          /\u003e\n        \u003c/Popup\u003e\n      \u003c/PopupLibrary\u003e\n    );\n  }\n}\n\nexport default register(PopupManager);\n```\n\n## Basic Usage\n\n### 1. Set up the store\nCreate a helper to set up an instance of [the `LinkedRenderStore`](https://rescribet.github.io/link-lib/classes/linkedrenderstore.html)\nto use in your application;\n```javascript\n// src/LRS.js\nimport { createNS } from '@ontologies/core';\nimport { createStore, DEFAULT_TOPOLOGY } from 'link-lib';\nimport { FRONTEND_URL } from './config';\n\n/**\n* We have a helper function to initialize the store, the first param is config (default should\n* suffice), the second is an array of middleware, the default terminating middleware calls the\n* schema:Action executor [`LRS#execActionByIRI`](https://github.com/rescribet/link-lib/wiki/Hypermedia-API).\n*/\nconst LRS = createStore({}, [\n  /**\n  * This middleware logs all actions passed.\n  * _lrs The live store once initialized.\n  * next The next middleware, not calling this will terminate the middleware flow for that action.\n  * (a, o) The action IRI and an unspecified payload respectively.\n  */\n  (lrs) =\u003e next =\u003e (a, o) =\u003e {\n      console.log(`Link action '${a}' passed down`);\n      return next(a, o);\n    },\n]);\n\n/**\n * If the app is programmed for a specific backend, it's useful to add it to the namespaces so that\n * it can be used for (hard-coded) entry points.\n */\nconst api = createNS(FRONTEND_URL);\n\n/**\n * Set up your own namespace for (virtual) app-specific properties (this might be the same as the\n * api). These SHOULD NOT be shared and have app-specific semantics.\n *\n * Virtual properties are useful for creating behaviour separate from data-source, while still using\n * the same interface.\n */\nconst app = createNS(FRONTEND_URL);\n\n/**\n* It's useful to have a central source of valid application topologies. This also provides a\n* location to document the intended usage.\n*\n* A common issue when beginning with link is forgetting to set the correct topology(ies), so\n* defaulting to registering views under all topologies can prevent a lot of headaches. Mind that\n* over-registering might cause the wrong view to be rendered rather than none at all.\n*/\nexport const allTopologies = [\n  // Generally used to mean that the resource is the main content on the page.\n  DEFAULT_TOPOLOGY,\n  // The resource/property is rendered within the navigation menu (e.g. as a `li`)\n  app('navigation'),\n  // The resource/prop is rendered in a table (e.g. as a single row within the table).\n  app('table'),\n  // The resource/prop is rendered in a row (e.g. as a single cell within the row).\n  app('row'),\n  // The resource/prop is rendered in a cell (e.g. as a raw value or some small representation).\n  app('cell'),\n];\n\n/**\n* Include this function so you can't forget a topology by registering all but those explicitly\n* handled by other components.\n*/\nexport function allTopologiesExcept(...topologies) {\n  const filtered = allTopologies.slice();\n  topologies.forEach((t) =\u003e {\n    const i = filtered.indexOf(t);\n    if (i !== -1) {\n      filtered.splice(i, 1);\n    }\n  });\n\n  return filtered;\n}\n\nexport default LRS;\n```\n\n### 2. Add some views\nWrite some views to render resources:\n\n```JSX harmony\nimport * as schema from '@ontologies/schema';\nimport LinkedRenderStore from 'link-lib';\nimport { link, register, Property, Resource } from 'link-redux';\nimport React from 'react';\nimport { Link } from 'react-router-dom';\nimport FontAwesome from 'react-fontawesome';\n\nimport { NS } from './LRS';\n\nclass Thing extends React.PureComponent {\n  static type = schema.Thing;\n\n  /**\n  * This function automatically wraps your component with the `link` method to bind your component\n  * to store data (like `connect` in redux).\n  */\n  static mapDataToProps = [\n    schema.text,\n    schema.creator,\n  ];\n\n  render() {\n    const { creator, text } = this.props;\n\n    return (\n      \u003cdiv\u003e\n        \u003cProperty label={schema.name}/\u003e // Delegate the rendering of the name to another dynamically resolved component\n        \u003cp\u003e{text.value}\u003c/p\u003e\n        \u003cResource subject={creator} /\u003e\n      \u003c/div\u003e\n    );\n  }\n}\n\nclass ThingNavigation extends React.PureComponent {\n  static type = schema.Thing;\n\n  static mapDataToProps = [schema.name];\n\n  static topology = app('navigation');\n\n  static linkOpts = {\n    returnType: 'value', // We don't need the objects, just their values for this view, see {link} for all options.\n  };\n\n  render() {\n    // The `subject` prop is always given to rendered views so it can link to itself.\n    const { name, subject } = this.props;\n\n    return (\n      \u003cli\u003e\n        \u003cLink to={subject}\u003e // Not to be confused with the {link} HOC.\n          {name}\n        \u003c/Link\u003e\n      \u003c/li\u003e\n    );\n  }\n}\n\n/**\n* Properties without being connected via the `link` function recieve their prop's value via `linkedProp`\n*\n* As you can see, components don't have to be classes, but can be function components as well.\n*/\nconst ThingName = ({ linkedProp }) =\u003e \u003ch1\u003e{linkedProp.value}\u003c/h1\u003e;\n\nThingName.type = schema.Thing;\nThingName.property = schema.name;\n/**\n * We'll bind all known topologies to this component, rather than the larger components since it has\n * less chance to overflow out of bounds.\n * We can use `undefined` here as a shortcut for the default as well (bound to the `Thing` component).\n */\nThingName.topology = allTopologiesExcept(undefined, app('navigation'), );\n\n// We want to show whether it's the users' birthday\nconst PersonName = ({ birthDay, name }) =\u003e {\n  const birthdayIcon = isToday(birthDay) ? \u003cFontAwesome name=\"birthday-cake\" /\u003e : null;\n\n  return \u003ch1\u003e{name}{birthdayIcon}\u003c/h1\u003e;\n}\n\n/**\n  * We need to wrap our component with the `register` call to make them acceptable for {LinkedRenderStore::registerRenderer}\n  * Note that it doesn't return a react component, but a plain object, so it can't be wrapped in HOC's after this.\n  */\nexport default [\n  register(Thing),\n  register(ThingNavigation),\n  register(ThingName),\n  /**\n  * The only thing `register` does is mapping the component and static properties onto the underlying\n  * function, so if you want your component to serve for multiple configurations, you can just call\n  * it manually.\n  */\n  LinkedRenderStore.registerRenderer(\n    link(\n      [schema.name, schema.birthDate],\n      { returnType: 'value' } // We can adjust whether we get passed (string) values, Nodes, Statements.\n    )(PersonName),\n    schema.Person,\n    schema.name,\n    app('topologies/preview')\n  ),\n];\n```\n\n### 3. Wire the views into the store\nRegister the views so link can resolve them when the data comes in;\n\n```JSX harmony\nimport LRS from './LRS';\n\nimport Thing from './views';\n\n/**\n* Notice the spread (...) operator, this is because the (default) export of `./views` returns an\n* array rather than a single ComponentRegistration array (returned by\n* LinkedRenderStore.registerRenderer), but `registerAll` doesn't handle deeply nested arrays, so\n* they should be spread either here, or in the views.\n*\n* CAUTION: This is the LinkedRenderStore app *instance*, not class from `link-lib`.\n*/\nLRS.registerAll(\n  ...Thing,\n);\n\n```\n\n### 4. Enable the store\nNow that we have views, lets enable link in the React tree with our `LinkedRenderStore` instance:\n\n```JSX harmony\nimport { RenderStoreProvider } from 'link-redux';\nimport { withRouter } from 'react-router';\nimport LRS from './LRS';\n\n// Either with react-router, or without and just take the current location (but listen for pushstate).\nconst App = ReactRouter\n  ? withRouter(() =\u003e \u003cResource subject={} /\u003e)\n  : () =\u003e \u003cResource subject={new NamedNode(window.location.href)} /\u003e;\n\nexport default () =\u003e (\n  \u003cRenderStoreProvider value={LRS}\u003e\n    \u003cRouter history={example}\u003e\n      \u003cApp /\u003e\n    \u003c/Router\u003e\n  \u003c/RenderStoreProvider\u003e\n);\n```\n\n### 5. Start the application\nIt should render (and fetch) resources passed as `subject` to a `Resource` (The\ntype of `subject` MUST be a NamedNode instance, e.g. `api('todos/5')`).\n\nNote that each `Resource` also acts as a [React error boundary](https://reactjs.org/docs/error-boundaries.html),\nso errors should automatically be contained rather than crash your entire app.\n\n## Further usage\n\n### Loading and error handling\nSince all data fetching is handled at deeper layers, link also exposes some API's to render different\nviews when an error occurred or when component is loading. Just register a component under\n`ll.ErrorResource` for error handling and `ll.LoadingResource` for loading states\nrespectively. Different views can be registered for different topologies as well.\n\nThe view for when no view could be found can be registered on the type `ll.NoView`\n\n```JSX Harmony\nclass ThingNavigation extends React.PureComponent {\n  // The magical type to catch errors.\n  static type = ll.ErrorResource;\n\n  // We can switch views so it renders with appropriate formatting for its location.\n  static topology = app('navigation');\n\n  render() {\n    // Components rendered when an error occur have some additional props.\n    const {\n      // The error caught (if any) if this was caused by an error boundary rather than a fetch error.\n      caughtError,\n      // The request status of the underlying resource, see {LinkedDataApi#getStatus}.\n      linkRequestStatus,\n      // Call to clear the {subject}'s data and fetch the resource again.\n      reloadLinkedObject,\n      // A function to reset the error state of the LRC. Useful in conjecture with reloadLinkedObject.\n      reset,\n      // The IRI of the resource being rendered.\n      subject,\n     } = this.props;\n\n    return (\n      \u003cli onClick={}\u003eSomething went wrong, click to try again\u003c/li\u003e\n    );\n  }\n}\n```\n\n### Datatype rendering\nIt's also possible to render different views for object literals, like you can resolve views for\nresources dynamically, especially handy when the range of the statement can be multiple types and\nmaintaining a giant switch statement isn't to your liking;\n\n```JSX Harmony\nimport rdf from '@ontologies/core';\n\nclass DollarTableCell extends React.PureComponent {\n  // Set the type to Literal to render individual values.\n  static type = rdf.Literal;\n\n  /**\n  * The `property` field now acts to resolve the data type rather than the predicate.\n  * In this case, the dbpedia `usDollar` type.\n  */\n  static property = dbdt.usDollar;\n\n  // Here too, we can adjust rendering for the appropriate context.\n  static topology = app('tableCell');\n\n  render() {\n    const literalVal = Number(this.props.linkedProp.value)\n\n    // Use some money library to format the currency\n    return (\n      \u003cReact.Fragment\u003e\n        {Money.format(literalVal, 'USD')}\n      \u003c/React.Fragment\u003e\n    );\n  }\n}\n```\n\n### String IRI to NamedNode\nIt sometimes happens that you recieve an IRI in string form (e.g. window.location.href) which needs\nto be converted to a link-enabled NamedNode, you MUST use the [patched version of rdflib](https://github.com/rescribet/rdfllib.js);\nfor this, since we work around the default RDFlib.js NamedNode constructor for performance reasons.\n\n### Multi-IRI\nMost components wouldn't be very useful if they can only render one type of term. Therefore, most of the methods accept\nan array of terms as well:\n```jsx harmony\nLinkedRenderStore.registerRenderer(Thing, [schema.Thing, owl.Thing]);\n```\n```jsx harmony\n\u003cProperty label={[schema.name, rdfs.label]} /\u003e\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frescribet%2Flink-redux","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frescribet%2Flink-redux","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frescribet%2Flink-redux/lists"}