{"id":21029918,"url":"https://github.com/kitconcept/plone.restapi-react-tutorial","last_synced_at":"2025-04-28T12:44:46.058Z","repository":{"id":51826403,"uuid":"91732753","full_name":"kitconcept/plone.restapi-react-tutorial","owner":"kitconcept","description":null,"archived":false,"fork":false,"pushed_at":"2021-05-10T16:39:31.000Z","size":320,"stargazers_count":2,"open_issues_count":3,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-24T04:56:07.162Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","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/kitconcept.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":"2017-05-18T19:59:59.000Z","updated_at":"2018-02-06T16:43:10.000Z","dependencies_parsed_at":"2022-08-23T10:40:10.756Z","dependency_job_id":null,"html_url":"https://github.com/kitconcept/plone.restapi-react-tutorial","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kitconcept%2Fplone.restapi-react-tutorial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kitconcept%2Fplone.restapi-react-tutorial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kitconcept%2Fplone.restapi-react-tutorial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kitconcept%2Fplone.restapi-react-tutorial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kitconcept","download_url":"https://codeload.github.com/kitconcept/plone.restapi-react-tutorial/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251315818,"owners_count":21569867,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":[],"created_at":"2024-11-19T12:15:02.278Z","updated_at":"2025-04-28T12:44:46.026Z","avatar_url":"https://github.com/kitconcept.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# plone.restapi-react-tutorial\n\nThis tutorial explains how to build a React-based JavaScript front-end application that queries a Plone REST API back-end to display the front-page of the Plone site.\n\n## Prerequisits\n\nBefore we can start, you need Node.js 6 and the Node package manager (npm) installed. Please check https://nodejs.org for install instructions.\nTo ease development with multiple Node versions, we recommend to use nvm (https://github.com/creationix/nvm).\n\nWe will also use the yarn package manager (https://yarnpkg.com/en/) which provides you with repeatable and stable builds, amongst other features. To install yarn globally run:\n\n```\n  $ npm install yarn -g\n```\n\nYou also need a Plone instance with plone.restapi installed. Here is a minimal buildout configuration:\n\n```\n[buildout]\nextends = http://dist.plone.org/release/5.0.7/versions.cfg\nparts = instance\n        plonesite\n\n[instance]\nrecipe = plone.recipe.zope2instance\nuser = admin:admin\nhttp-address = 8080\neggs =\n    Plone\n    plone.restapi\n\nzcml-additional =\n  \u003cconfigure xmlns=\"http://namespaces.zope.org/zope\"\n            xmlns:plone=\"http://namespaces.plone.org/plone\"\u003e\n  \u003cplone:CORSPolicy\n    allow_origin=\"http://localhost:4300,http://127.0.0.1:4300,http://localhost:3000,http://127.0.0.1:3000\"\n    allow_methods=\"DELETE,GET,OPTIONS,PATCH,POST,PUT\"\n    allow_credentials=\"true\"\n    expose_headers=\"Content-Length,X-My-Header\"\n    allow_headers=\"Accept,Authorization,Content-Type,X-Custom-Header\"\n    max_age=\"3600\"\n    /\u003e\n  \u003c/configure\u003e\n\n[plonesite]\nrecipe = collective.recipe.plonesite\nsite-id = Plone\ninstance = instance\nprofiles-initial = Products.CMFPlone:dependencies\nprofiles =\n    plonetheme.barceloneta:default\n    plone.app.contenttypes:plone-content\n    plone.restapi:default\nupgrade-portal = False\nupgrade-all-profiles = False\nsite-replace = True\n```\n\nThe instance section contains a zcml-additional configuration that defines a CORS policy that allows the React application to connection to our Plone backend. See https://pypi.python.org/pypi/plone.rest for further details if necessary.\n\nThe 'plonesite' section has been added as for convenience, to create a Plone instance during the buildout run.\n\n\n## Create React App\n\nTo create our first React project, we have to install the [Create React App](https://github.com/facebookincubator/create-react-app):\n\n```\n  $ yarn add create-react-app -g\n```\n\nWe install create-react-app globally (by using the '-g' paramater) to make the 'create-react-app' command available on our command line.\nCreate a new React application with the name 'plone.restapi-react-tutorial' by:\n\n```\n  $ yarn create-react-app plone.restapi-react-tutorial\n```\n\nNow open the 'plone.restapi-react-tutorial' folder in your prefered editor and check the files that have been created.\n\nThe index.html file in the 'public' folder is just a static HTML file that contains a single element with the id 'root' to bootstrap the JavaScript application:\n\n```\n  \u003cdiv id=\"root\"\u003e\u003c/div\u003e\n```\n\nWebpack will take care of injecting the necessary JavaScript.\nThe JavaScript entry point is the 'index.js' in the '/src' folder with the following contents:\n\n```\n  import React from 'react';\n  import ReactDOM from 'react-dom';\n  import App from './App';\n  import './index.css';\n\n  ReactDOM.render(\n    \u003cApp /\u003e,\n    document.getElementById('root')\n  );\n```\n\nThe 'ReactDom.render' method takes the application element as a first parameter and the DOM-element where it is rendered as a second argument.\n\nThe application element `\u003cApp /\u003e` is imported from the App.js file that you find in your src folder:\n\n```\n  import React, { Component } from 'react';\n  import logo from './logo.svg';\n  import './App.css';\n\n  class App extends Component {\n    render() {\n      return (\n        \u003cdiv className=\"App\"\u003e\n          \u003cdiv className=\"App-header\"\u003e\n            \u003cimg src={logo} className=\"App-logo\" alt=\"logo\" /\u003e\n            \u003ch2\u003eWelcome to React\u003c/h2\u003e\n          \u003c/div\u003e\n          \u003cp className=\"App-intro\"\u003e\n            To get started, edit \u003ccode\u003esrc/App.js\u003c/code\u003e and save to reload.\n          \u003c/p\u003e\n        \u003c/div\u003e\n      );\n    }\n  }\n\n  export default App;\n```\n\nThis is a very simple React component.\nIt is a regular JavaScript class that extends the 'Component' class from react.\nIts 'render' method is supposed to just return basic HTML.\n\nVariables can be injected into the HTML by using curly braces '{}'.\nThe logo that we import with:\n\n```\n  import logo from './logo.svg';\n```\n\nIt is injected into the 'src' attribute of the image tag:\n\n```\n  \u003cimg src={logo} className=\"App-logo\" alt=\"logo\" /\u003e\n```\n\nTo start the React app, we `cd` into the directory we created:\n\n```\n  $ cd plone.restapi-react-tutorial/\n```\n\nThen build the React application by running:\n\n```\n  $ yarn install\n```\n\nTo start your application run:\n\n```\n  $ yarn start\n```\n\nYour browser will automatically open the react app on 'localhost:3000'.\nThe app will automatically watch the files in the '/src' folder of your app and reload the application in your browser immediately.\n\nTry, for instance, to change the headline in 'src/App.js' from:\n\n```\n  \u003ch2\u003eWelcome to React\u003c/h2\u003e\n```\n\nto:\n\n```\n  \u003ch2\u003eWelcome to Plone\u003c/h2\u003e\n```\n\nThis works for HTML, JavaScript and any style changes in your app.\n\ncreate-react-app also comes with a Jest-based test setup and an example test that you can run with:\n\n```\n  $ yarn test\n```\n\n## Connecting to plone.restapi\n\nWith the basic application structure in place, we can now start to connect to plone.restapi.\nWe are going to make a single call to the Plone backend to retrieve the front-page of our Plone site and display it within our React app.\n\nOne of the core principles of React is a uni-directional data flow of component state. We define a constructor method, that sets the inital state of our component.\n\nAdd the following lines before the render function inside our App Component class:\n\n```\n  constructor(){\n    super();\n    this.state={\n      page: {}\n    };\n  }\n```\n\nWhen our component has been instantiated properly, we want retrieve the Plone front-page via plone.restapi. A React component provides certain lifecycle events that can be used to do the back-end call.\n\n**Note:** *This very basic example violates the single responsibility pattern that is considered best practice in the React community. A React component should only be responsible for one thing. Therefore in a real-world application, we would move the API call to a 'container component' that does the actual API call and use component props to pass it to the component that actually displays the content.*\n\nWe use the componentDidMount lifecycle event that is fired after the succesful instantiation of the React component and the ES6 fetch API to do the call to the backend.\n\n**Note:** *React does not make any assumptions about what libraries you use to query the backend. You could as well use RxJs or any other library to do the call.*\n\nAdd the following lines after our constructor definition, before the render function and start your Plone instance:\n\n```\n  componentDidMount(){\n    fetch(\n      'http://localhost:8080/Plone/front-page',\n      {\n        headers: {\n          'Accept': 'application/json',\n        }\n      }\n    )\n    .then((response) =\u003e response.json())\n    .then((responseData) =\u003e {\n      this.setState({page: responseData});\n      console.log('Fetch from plone.restapi successful!')\n    })\n    .catch((error) =\u003e {\n      console.log('Error fetching and parsing data', error);\n    });\n  }\n```\n\nThe URL that we query for the backend call is similar to the URL you would use in a browser to retrieve the front-page in your browser. plone.restapi uses content negotiation to tell Plone that it actually would like to get a JSON response. This is done by sending the HTTP header 'Accept' with the value 'application/json'.\n\nCheck your browser console and the network tab of your developer tools to make sure the API call was actually successful.\nWith the successful API call in place we can now use the data that has been successfully stored in our component state variable.\n\nState variables that we defined in our constructor method and that has been filled with actual data in our componentsDidMount method can be accessed in the component render method now.\n\nReplace the Hello React headline with the title of the Plone front page:\n\n```\n\u003ch2\u003e{this.state.page.title}\u003c/h2\u003e\n```\n\nWe can also show the description and the body text of the front-page.\n\nAdd the following lines inside the return function of your render method before the closing div:\n\n```\n  class AppContainer extends Component {\n\n    ...\n\n    render(){\n      return (\n\n        ...\n\n        {\n          this.state.page.description \u0026\u0026\n          \u003ch3\u003e{this.state.page.description}\u003c/h3\u003e\n        }\n        {\n          this.state.page.text \u0026\u0026\n          \u003cp dangerouslySetInnerHTML={{ __html: this.state.page.text.data }} /\u003e\n        }\n        ...\n      );\n    }\n  }\n```\n\n**Note:** *The ``\u0026\u0026``prevents the app from failing if those variables are not available.*\n\n**Note:** *CORS (Cross-origin resource sharing) standard in HTML5 specifies which origins are permitted to request resources on the server. This may block the fetch request made to the local instance of Plone REST API. So for development purposes, extensions such as 'Access-Control-Allow-Origin' on Chrome can be used to temporarily override and send cross-domain requests directly.*\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkitconcept%2Fplone.restapi-react-tutorial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkitconcept%2Fplone.restapi-react-tutorial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkitconcept%2Fplone.restapi-react-tutorial/lists"}