{"id":344,"date":"2017-08-30T15:40:13","date_gmt":"2017-08-30T13:40:13","guid":{"rendered":"http:\/\/localhost:8888\/wordpress\/?p=344"},"modified":"2017-08-30T17:47:59","modified_gmt":"2017-08-30T15:47:59","slug":"react-projektin-perustaminen","status":"publish","type":"post","link":"https:\/\/www.pilvikoodari.net\/?p=344","title":{"rendered":"React -projektin perustaminen"},"content":{"rendered":"<p><a href=\"https:\/\/www.pilvikoodari.net\/?p=363\" target=\"_blank\" rel=\"noopener noreferrer\">React esittely<\/a> -blogikirjoituksessa esiteltiin Reactin perusteita yksinkertaisella toteutuksella, jossa React ladattiin k\u00e4ytt\u00f6\u00f6n ns. <a href=\"https:\/\/en.wikipedia.org\/wiki\/Content_delivery_network\" target=\"_blank\" rel=\"noopener noreferrer\">CDN<\/a> -latauksella. HTML-sivun l\u00e4hdekoodiin koodattiin suoraan Javascripti\u00e4 joka k\u00e4ytti React -kirjastoa. T\u00e4m\u00e4 tapa sopii keve\u00e4\u00e4n Reactin esittelyyn, mutta v\u00e4h\u00e4nk\u00e4\u00e4n vakavammassa projektissa t\u00e4m\u00e4 ei ole ollenkaan riitt\u00e4v\u00e4 tapa\u00a0k\u00e4ytt\u00e4\u00e4 Reactia.<\/p>\n<p>T\u00e4ss\u00e4 blogikirjoituksessa perustamme oikean React -kehitysymp\u00e4rist\u00f6n, jossa kehitysty\u00f6 onnistuu helposti ja tehokkaasti.\u00a0Koodausty\u00f6 tapahtuu JavaScript -tiedostojen kautta ja kehitt\u00e4j\u00e4 saa k\u00e4ytt\u00f6\u00f6ns\u00e4 ty\u00f6kaluja jotka helpottavat kehitysty\u00f6t\u00e4.<\/p>\n<p><strong>create-react-app &#8211; n\u00e4pp\u00e4r\u00e4 ty\u00f6kalu projektin perustamiseen<\/strong><\/p>\n<p>T\u00e4ysiverinen React -kehitysymp\u00e4rist\u00f6 k\u00e4ytt\u00e4\u00e4 useita erilaisia\u00a0ohjelmia, jotka yhdess\u00e4 muodostavat nykyaikaisen kehitysymp\u00e4rist\u00f6n. Kehityksess\u00e4 tarvitaan mm. HTTP-palvelinta jolla kehitysversio py\u00f6rii paikallisesti sek\u00e4 k\u00e4\u00e4nt\u00e4j\u00e4\u00e4 joka k\u00e4\u00e4nt\u00e4\u00e4 uusimmilla JavaScriptin versioilla koodattua JavaScript -koodia webbiselaimien ymm\u00e4rt\u00e4m\u00e4\u00e4n, riitt\u00e4v\u00e4n yhteensopivaan (=&#8221;riitt\u00e4v\u00e4n vanhaan&#8221;)\u00a0JavaScriptin muotoon.<\/p>\n<p>Jotta projektin perustaminen helpottuisi, k\u00e4yt\u00e4mme projektin perustamiseen n\u00e4pp\u00e4r\u00e4\u00e4 apuohjelmaa nimelt\u00e4\u00a0<a href=\"https:\/\/github.com\/facebookincubator\/create-react-app\" target=\"_blank\" rel=\"noopener noreferrer\">create-react-app<\/a>. Itse asiassa create-react-app on npm -kirjasto, joka asennetaan omalle koneelle globaalisti ajettavaksi ohjelmaksi. Asentaminen tapahtuu npm:ll\u00e4 seuraavasti:<\/p>\n<pre class=\"nums:false lang:sh decode:true \" title=\"create-react-app asennus\">npm install -g create-react-app<\/pre>\n<p>Yll\u00e4 oleva komento asentaa create-react-app:n siten ett\u00e4 se voidaan ajaa mist\u00e4 tahansa hakemistosta k\u00e4sin.<\/p>\n<p>Siirryt\u00e4\u00e4n seuraavaksi sopivaan hakemistoon, jonne haluamme projektihakemiston perustettavan ja suoritetaan create-react-app.<\/p>\n<pre class=\"nums:false lang:sh decode:true \" title=\"Projektin perustaminen\">$ cd projektit\r\n\r\n$ create-react-app react-demoilua<\/pre>\n<p>Projekti perustetaan ja npm:n kautta haetaan projektissa tarvittavat kirjastot. Lopulta projekti on valmis k\u00e4ytt\u00f6\u00f6n:<\/p>\n<p><a href=\"https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-06-05-at-19.38.27.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-447 size-large\" src=\"https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-06-05-at-19.38.27-1024x662.png\" alt=\"\" width=\"1024\" height=\"662\" srcset=\"https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-06-05-at-19.38.27-1024x662.png 1024w, https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-06-05-at-19.38.27-300x194.png 300w, https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-06-05-at-19.38.27-768x496.png 768w, https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-06-05-at-19.38.27.png 1646w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/p>\n<p>Kuten viimeisest\u00e4 ohjeesta n\u00e4kyy, React -ohjelma on valmis k\u00e4ynnistett\u00e4v\u00e4ksi. Suoritetaan siis komennot:<\/p>\n<pre class=\"nums:false lang:sh decode:true \" title=\"k\u00e4ynnistys\">cd react-demoilua\r\n\r\nnpm start<\/pre>\n<p>Ohjelma k\u00e4ynnistyy ja selaimeen aukeaa osoite http:\/\/localhost:3000:<\/p>\n<p><a href=\"https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-06-05-at-19.45.29.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-448\" src=\"https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-06-05-at-19.45.29-1024x795.png\" alt=\"\" width=\"1024\" height=\"795\" srcset=\"https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-06-05-at-19.45.29-1024x795.png 1024w, https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-06-05-at-19.45.29-300x233.png 300w, https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-06-05-at-19.45.29-768x596.png 768w, https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-06-05-at-19.45.29.png 1394w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/p>\n<p>Konsolissa n\u00e4kyy seuraava teksti:<\/p>\n<p><a href=\"https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-06-05-at-19.49.11.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-449\" src=\"https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-06-05-at-19.49.11-1024x678.png\" alt=\"\" width=\"1024\" height=\"678\" srcset=\"https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-06-05-at-19.49.11-1024x678.png 1024w, https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-06-05-at-19.49.11-300x199.png 300w, https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-06-05-at-19.49.11-768x508.png 768w, https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-06-05-at-19.49.11.png 1254w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/p>\n<p>Mit\u00e4 kaikkea edell\u00e4 tapahtui? Tutkitaan ensin projektihakemiston sis\u00e4lt\u00f6\u00e4:<\/p>\n<p><a href=\"https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-06-05-at-19.52.20.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-large wp-image-450\" src=\"https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-06-05-at-19.52.20-1024x822.png\" alt=\"\" width=\"1024\" height=\"822\" srcset=\"https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-06-05-at-19.52.20-1024x822.png 1024w, https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-06-05-at-19.52.20-300x241.png 300w, https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-06-05-at-19.52.20-768x616.png 768w, https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-06-05-at-19.52.20.png 1366w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/p>\n<p>N\u00e4emme, ett\u00e4 hakemiston\u00a0rakenne ja sis\u00e4lt\u00f6 on itse asiassa varsin yksinkertainen:<\/p>\n<p><strong><em>.gitignore<\/em><\/strong> on Git-versionhallintaa varten perustettu ns. <em>ignore<\/em> tiedosto, jossa m\u00e4\u00e4ritell\u00e4\u00e4n mit\u00e4\u00a0tiedostoja hakemiston alla ei\u00a0ole tarpeen laittaa versionhallintaan.<\/p>\n<p><strong><em>package.json<\/em><\/strong> -tiedosto on React -ohjelman syd\u00e4n. Siell\u00e4 on m\u00e4\u00e4ritetty npm -ohjelman avulla ajettavat skriptit sek\u00e4 ohjelman riippuvuudet ja muut m\u00e4\u00e4ritykset. Tutustutaan tiedostoon my\u00f6hemmin tarkemmin.<\/p>\n<p><em><strong>node_modules<\/strong> &#8211;<\/em>hakemistoon\u00a0ladataan ohjelman k\u00e4ytt\u00e4m\u00e4t npm -paketit eli ohjelman k\u00e4ytt\u00e4m\u00e4t riippuvuudet.<\/p>\n<p><strong>public<\/strong> -hakemistossa on<em> index.html<\/em> tiedosto, josta ohjelman suoritus k\u00e4ynnistyy.<\/p>\n<p><em><strong>README.md<\/strong> &#8211;<\/em>tiedostoon voidaan dokumentoida projektiin liittyv\u00e4\u00e4 ohjeistusta, joka n\u00e4kyy automaattisesti kun projektia katselee esim.\u00a0GitHub-palvelussa.<\/p>\n<p>Mielenkiintoisin osa sijaitsee <strong><em>src<\/em><\/strong> -hakemistossa. Siell\u00e4 on ohjelman l\u00e4hdekoodi ja t\u00e4m\u00e4 hakemisto on paikka, jossa ohjelman kehitys tulee oikeastaan kokonaan tapahtumaan.<\/p>\n<p>Tutustutaan ensin <strong><em>package.json<\/em><\/strong> tiedostoon:<\/p>\n<pre class=\"lang:js decode:true \" title=\"package.json\">{\r\n  \"name\": \"react-demoilua\",\r\n  \"version\": \"0.1.0\",\r\n  \"private\": true,\r\n  \"dependencies\": {\r\n    \"react\": \"^15.5.4\",\r\n    \"react-dom\": \"^15.5.4\"\r\n  },\r\n  \"devDependencies\": {\r\n    \"react-scripts\": \"1.0.7\"\r\n  },\r\n  \"scripts\": {\r\n    \"start\": \"react-scripts start\",\r\n    \"build\": \"react-scripts build\",\r\n    \"test\": \"react-scripts test --env=jsdom\",\r\n    \"eject\": \"react-scripts eject\"\r\n  }\r\n}<\/pre>\n<p>Yleiskuva <em>package.json<\/em> -tiedostosta on varsin yksinkertainen. T\u00e4m\u00e4 johtuu siit\u00e4 ett\u00e4 create-react-app piilottaa projektin todelliset riippuvuudet allensa varsin tehokkaasti. Palataan t\u00e4h\u00e4n my\u00f6hemmin mutta tutustutaan ensin tiedostoon t\u00e4ss\u00e4 muodossa.<\/p>\n<p>Tiedostossa on ensin \u00a0projektin metatietoa ja sen j\u00e4lkeen riveill\u00e4 5-8 ohjelman k\u00e4ytt\u00e4m\u00e4t riippuvuudet eli <em>dependencies (&#8221;depsit&#8221;)<\/em>. N\u00e4emme ett\u00e4 riippuvuuksia on t\u00e4ss\u00e4 vaiheessa ainoastaan Reactiin. Kehitysaikaiset riippuvuudet eli\u00a0<em>devDependencies<\/em> on m\u00e4\u00e4ritetty riveill\u00e4 9-11.\u00a0\u00a0Siell\u00e4 n\u00e4kyy <em>react-scripts<\/em>, joka itse asiassa on create-react-app:iin liittyv\u00e4 skriptikirjasto.<\/p>\n<p>Scripts -osiossa n\u00e4kyy komennot, jotka voidaan suorittaa ajamalla komento <em>npm\u00a0[komento]:<\/em><\/p>\n<p><em>npm\u00a0start\u00a0<\/em>k\u00e4ynnist\u00e4\u00e4 kehitysymp\u00e4rist\u00f6n,<em>\u00a0npm run build<\/em>\u00a0rakentaa tuotantopaketin ohjelmasta,\u00a0<em>npm test <\/em>suorittaa testit.<\/p>\n<p><em>npm run eject\u00a0<\/em>on tarkoitettu edistyneemmille k\u00e4ytt\u00e4jille (tai oikeastaan v\u00e4h\u00e4nk\u00e4\u00e4n vakavamman koodauksen harrastajalle): eject komennolla create-react-app -projektin rakenne voidaan &#8221;vapauttaa&#8221; kehitt\u00e4j\u00e4n k\u00e4ytt\u00f6\u00f6n siten ett\u00e4 kaikki kehitysymp\u00e4rist\u00f6n komponentit ovat vapaasti konfiguroitavissa ja n\u00e4ht\u00e4vill\u00e4. \u00a0T\u00e4ll\u00f6in tosin menetet\u00e4\u00e4n react-create-app:n tarjoama\u00a0helppok\u00e4ytt\u00f6isyys mutta toisaalta osaava kehitt\u00e4j\u00e4 saa k\u00e4ytt\u00f6\u00f6ns\u00e4 huomattavasti\u00a0laajemman s\u00e4\u00e4dett\u00e4vyyden. T\u00e4ss\u00e4 esimerkki, millaiseksi package.json muotoutuu, kun ajetaan\u00a0<em>npm run eject<\/em>:<\/p>\n<pre class=\"lang:js decode:true\" title=\"T\u00e4ysimuotoinen package.json\">{\r\n  \"name\": \"react-demoilua\",\r\n  \"version\": \"0.1.0\",\r\n  \"private\": true,\r\n  \"dependencies\": {\r\n    \"react\": \"^15.5.4\",\r\n    \"react-dom\": \"^15.5.4\"\r\n  },\r\n  \"devDependencies\": {\r\n    \"autoprefixer\": \"7.1.0\",\r\n    \"babel-core\": \"6.24.1\",\r\n    \"babel-eslint\": \"7.2.3\",\r\n    \"babel-jest\": \"20.0.3\",\r\n    \"babel-loader\": \"7.0.0\",\r\n    \"babel-preset-react-app\": \"^3.0.0\",\r\n    \"babel-runtime\": \"6.23.0\",\r\n    \"case-sensitive-paths-webpack-plugin\": \"1.1.4\",\r\n    \"chalk\": \"1.1.3\",\r\n    \"css-loader\": \"0.28.1\",\r\n    \"dotenv\": \"4.0.0\",\r\n    \"eslint\": \"3.19.0\",\r\n    \"eslint-config-react-app\": \"^1.0.4\",\r\n    \"eslint-loader\": \"1.7.1\",\r\n    \"eslint-plugin-flowtype\": \"2.33.0\",\r\n    \"eslint-plugin-import\": \"2.2.0\",\r\n    \"eslint-plugin-jsx-a11y\": \"5.0.3\",\r\n    \"eslint-plugin-react\": \"7.0.1\",\r\n    \"extract-text-webpack-plugin\": \"2.1.0\",\r\n    \"file-loader\": \"0.11.1\",\r\n    \"fs-extra\": \"3.0.1\",\r\n    \"html-webpack-plugin\": \"2.28.0\",\r\n    \"jest\": \"20.0.3\",\r\n    \"object-assign\": \"4.1.1\",\r\n    \"postcss-flexbugs-fixes\": \"3.0.0\",\r\n    \"postcss-loader\": \"2.0.5\",\r\n    \"promise\": \"7.1.1\",\r\n    \"react-dev-utils\": \"^3.0.0\",\r\n    \"react-error-overlay\": \"^1.0.7\",\r\n    \"style-loader\": \"0.17.0\",\r\n    \"sw-precache-webpack-plugin\": \"0.9.1\",\r\n    \"url-loader\": \"0.5.8\",\r\n    \"webpack\": \"2.6.1\",\r\n    \"webpack-dev-server\": \"2.4.5\",\r\n    \"webpack-manifest-plugin\": \"1.1.0\",\r\n    \"whatwg-fetch\": \"2.0.3\"\r\n  },\r\n  \"scripts\": {\r\n    \"start\": \"node scripts\/start.js\",\r\n    \"build\": \"node scripts\/build.js\",\r\n    \"test\": \"node scripts\/test.js --env=jsdom\"\r\n  },\r\n  \"jest\": {\r\n    \"collectCoverageFrom\": [\r\n      \"src\/**\/*.{js,jsx}\"\r\n    ],\r\n    \"setupFiles\": [\r\n      \"&lt;rootDir&gt;\/config\/polyfills.js\"\r\n    ],\r\n    \"testMatch\": [\r\n      \"&lt;rootDir&gt;\/src\/**\/__tests__\/**\/*.js?(x)\",\r\n      \"&lt;rootDir&gt;\/src\/**\/?(*.)(spec|test).js?(x)\"\r\n    ],\r\n    \"testEnvironment\": \"node\",\r\n    \"testURL\": \"http:\/\/localhost\",\r\n    \"transform\": {\r\n      \"^.+\\\\.(js|jsx)$\": \"&lt;rootDir&gt;\/node_modules\/babel-jest\",\r\n      \"^.+\\\\.css$\": \"&lt;rootDir&gt;\/config\/jest\/cssTransform.js\",\r\n      \"^(?!.*\\\\.(js|jsx|css|json)$)\": \"&lt;rootDir&gt;\/config\/jest\/fileTransform.js\"\r\n    },\r\n    \"transformIgnorePatterns\": [\r\n      \"[\/\\\\\\\\]node_modules[\/\\\\\\\\].+\\\\.(js|jsx)$\"\r\n    ],\r\n    \"moduleNameMapper\": {\r\n      \"^react-native$\": \"react-native-web\"\r\n    }\r\n  },\r\n  \"babel\": {\r\n    \"presets\": [\r\n      \"react-app\"\r\n    ]\r\n  },\r\n  \"eslintConfig\": {\r\n    \"extends\": \"react-app\"\r\n  }\r\n}\r\n<\/pre>\n<p><em>eject\u00a0<\/em>on yksisuuntainen eli kerran ejektoitua projektia ei voi en\u00e4\u00e4n muuttaa takaisin yksinkertaisempaan muotoon. Mutta edelleen olellisimmat komennot\u00a0<em>npm start <\/em>sek\u00e4\u00a0<em>npm run build<\/em> ovat k\u00e4ytett\u00e4viss\u00e4. Joten ei ole merkityst\u00e4, ajatko t\u00e4ss\u00e4 vaiheessa ejekti\u00e4 vai et.<\/p>\n<p>Ejektin j\u00e4ljilt\u00e4 voimme kuitenkin havaita, ett\u00e4 React -projektissamme on nyt k\u00e4yt\u00f6ss\u00e4 useita\u00a0komponentteja erilaisiin tarkotuksiin, muun muassa:<\/p>\n<p><a href=\"https:\/\/babeljs.io\/\" target=\"_blank\" rel=\"noopener noreferrer\">Babel<\/a> -k\u00e4\u00e4nt\u00e4jill\u00e4 k\u00e4\u00e4nnet\u00e4\u00e4n JavaScript -ohjelmakoodia selaimelle sopivaan muotoon.<\/p>\n<p><a href=\"http:\/\/eslint.org\/\" target=\"_blank\" rel=\"noopener noreferrer\">ESLint<\/a> -kirjastot on tarkoitettu koodaajan apuv\u00e4lineeksi: ne tarjoavat automaattisen koodityylin tarkkailijan, jonka avulla koodaaja n\u00e4kee heti jos koodissa on joko tyylillisesti huonoja koodirivej\u00e4 (esim. sisennykset pieless\u00e4) tai jos koodissa k\u00e4ytet\u00e4\u00e4n joitain huonoksi tiedettyj\u00e4 tapoja (esim. m\u00e4\u00e4ritet\u00e4\u00e4n muuttuja jota ei oikeasti k\u00e4ytet\u00e4 ollenkaan).<\/p>\n<p><a href=\"https:\/\/webpack.github.io\/\" target=\"_blank\" rel=\"noopener noreferrer\"> WebPack<\/a> -&#8221;bundleria&#8221; k\u00e4ytet\u00e4\u00e4n pakkaamaan ohjelman sis\u00e4lt\u00e4m\u00e4 kokonaisuus yhdeksi kokonaisuudeksi. Lis\u00e4ksi se sis\u00e4lt\u00e4\u00e4 k\u00e4tev\u00e4n <em>Webpack\u00a0Development Server<\/em> -palvelimen. Se toimii\u00a0yksinkertaistetusti sanottuna kehitysaikaisena HTTP -palvelimena joka tarjoilee sovelluksen uusimman version selaimelle osoitteessa<em> http:\/\/localhost:3000<\/em>. Se osaa my\u00f6s ottaa uusimmat koodit k\u00e4ytt\u00f6\u00f6n &#8221;lennossa&#8221; joten se soveltuu erinomaisesti kehitysaikaiseen ty\u00f6skentelyyn: kun ohjelmakoodia muutetaan, muutos n\u00e4kyy v\u00e4litt\u00f6m\u00e4sti selaimessa!<\/p>\n<p>Joka tapauksessa: kehitysymp\u00e4rist\u00f6mme on nyt valmis k\u00e4ytt\u00f6\u00f6n ja voimme alkaa koodiin tutustumisen!<\/p>\n<p><strong>React -projektin rakenne ja toiminta<\/strong><\/p>\n<p>Avataan projekti jollain koodieditorilla, kuten JavaScript -koodarien suosimalla\u00a0<a href=\"https:\/\/code.visualstudio.com\/\" target=\"_blank\" rel=\"noopener noreferrer\">Visual Studio Code<\/a> -editorilla.<\/p>\n<p>Jos tutkitaan <em>public<\/em> -hakemistossa olevaan<em> index.html<\/em> -tiedostoa, havaitaan ett\u00e4 siell\u00e4 ei juurikaan ole sis\u00e4lt\u00f6\u00e4 body -tagin sis\u00e4ll\u00e4:<\/p>\n<pre class=\"lang:xhtml decode:true\" title=\"index.html\">&lt;body&gt;\r\n  &lt;noscript&gt;\r\n    You need to enable JavaScript to run this app.\r\n  &lt;\/noscript&gt;\r\n  &lt;div id=\"root\"&gt;&lt;\/div&gt;\r\n&lt;\/body&gt;<\/pre>\n<p>Body -osiossa on kuitenkin SPA eli <a href=\"https:\/\/en.wikipedia.org\/wiki\/Single-page_application\" target=\"_blank\" rel=\"noopener noreferrer\">Singe Page Application<\/a> -web ohjelmalle riitt\u00e4v\u00e4 osuus: div -elementti (jonka id on\u00a0<em>root). <\/em>T\u00e4h\u00e4n kohtaan sivulla React -ohjelma lis\u00e4\u00e4 HTML- ja JavaScript -sis\u00e4lt\u00f6\u00e4 ohjelmallisesti. React -ohjelmaa kehitett\u00e4ess\u00e4 HTML -koodausta ei ole tarpeen edes k\u00e4ytt\u00e4\u00e4 t\u00e4m\u00e4n enemp\u00e4\u00e4 vaan kaikki koodaus tehd\u00e4\u00e4n JavaScriptill\u00e4 sek\u00e4 <a href=\"https:\/\/www.tutorialspoint.com\/reactjs\/reactjs_jsx.htm\" target=\"_blank\" rel=\"noopener noreferrer\">JSX<\/a> -kielell\u00e4. Kun t\u00e4m\u00e4 index-sivu avataan kerran, selaimen ei ole sen j\u00e4lkeen tarvetta tehd\u00e4 sivulatausta, koska sivun \u00a0sis\u00e4lt\u00f6\u00e4 pystyt\u00e4\u00e4n muokkaamaan ohjelmallisesti &#8221;lennossa&#8221;. T\u00e4m\u00e4 tarjoaa k\u00e4ytt\u00e4j\u00e4lle huomattavasti\u00a0mukavemman k\u00e4ytt\u00f6kokemuksen verrattuna &#8221;vanhanaikaiseen&#8221; web-sovellukseen jossa toiminnon suorittaminen aiheutti aina sivunlatauksen.<\/p>\n<p>Tutkitaan seuraavaksi paikkaa, joka sijoittaa sivulle sen sis\u00e4ll\u00f6n. Avataan <em>src<\/em> -hakemistosta tiedosto index.js:<\/p>\n<pre class=\"lang:js decode:true\" title=\"index.js\">import React from 'react';\r\nimport ReactDOM from 'react-dom';\r\nimport App from '.\/App';\r\nimport registerServiceWorker from '.\/registerServiceWorker';\r\nimport '.\/index.css';\r\n\r\nReactDOM.render(&lt;App \/&gt;, document.getElementById('root'));\r\nregisterServiceWorker();\r\n<\/pre>\n<p>Sielt\u00e4 n\u00e4hd\u00e4\u00e4n, ett\u00e4 <em>root<\/em> -elementtiin render\u00f6id\u00e4\u00e4n eli &#8221;piirret\u00e4\u00e4n&#8221; App -tiedostossa oleva React -komponentti.<\/p>\n<p>Tutkitaan siis seuraavaksi\u00a0<em>App.js &#8211;<\/em>tiedostoa<em>:<\/em><\/p>\n<pre class=\"lang:js decode:true\" title=\"App.js\">import React, { Component } from 'react';\r\nimport logo from '.\/logo.svg';\r\nimport '.\/App.css';\r\n\r\nclass App extends Component {\r\n  render() {\r\n    return (\r\n      &lt;div className=\"App\"&gt;\r\n        &lt;div className=\"App-header\"&gt;\r\n          &lt;img src={logo} className=\"App-logo\" alt=\"logo\" \/&gt;\r\n          &lt;h2&gt;Welcome to React&lt;\/h2&gt;\r\n        &lt;\/div&gt;\r\n        &lt;p className=\"App-intro\"&gt;\r\n          To get started, edit &lt;code&gt;src\/App.js&lt;\/code&gt; and save to reload.\r\n        &lt;\/p&gt;\r\n      &lt;\/div&gt;\r\n    );\r\n  }\r\n}\r\n\r\nexport default App;<\/pre>\n<p>T\u00e4ss\u00e4 n\u00e4emmekin jo React -komponentin <em>App<\/em>. Se on koko ohjelmamme p\u00e4\u00e4komponentti, jonka <em>render<\/em> -funktio tuottaa sis\u00e4ll\u00f6n aiemmin tutkittuun index.html tiedostoon. <em>Render<\/em> -funktiossa k\u00e4ytet\u00e4\u00e4n JSX -kielt\u00e4 jossa on tuttuja elementtej\u00e4\u00a0HTML -kielest\u00e4.<\/p>\n<p><em>App.js<\/em> -tiedoston alussa importoidaan k\u00e4ytt\u00f6\u00f6n Reactin <em>Component<\/em> -luokka, yksi kuvatiedosto sek\u00e4 tyylit erillisest\u00e4 tyylitiedostosta.<\/p>\n<p>React -ohjelman kehitt\u00e4minen siis l\u00e4htee t\u00e4st\u00e4 <em>App.js<\/em> -tiedostosta eteenp\u00e4in! Koodarin teht\u00e4v\u00e4n\u00e4 on suunnitella ohjelman k\u00e4ytt\u00f6liittym\u00e4suunnitelman perusteella\u00a0uusia React -komponentteja ja sijoittaa niit\u00e4 n\u00e4yt\u00f6lle. K\u00e4yt\u00e4nn\u00f6ss\u00e4 koodari siis toteuttaa uusia tiedostoja, joissa on Reactin <em>Component<\/em> -luokasta peritty, aivan kuteen <em>App.js<\/em> -tiedostossa. Ohjelman rakenne pyrit\u00e4\u00e4n m\u00e4\u00e4ritt\u00e4m\u00e4\u00e4n osissa ja &#8221;n\u00e4yt\u00f6t&#8221; muodostetaan yhdistelem\u00e4ll\u00e4 komponentteja kokonaisuudeksi. Tiedostoon voi sis\u00e4llytt\u00e4\u00e4 toisia komponentteja kun ottaa ne k\u00e4ytt\u00f6\u00f6n <em>import<\/em> -menetelm\u00e4ll\u00e4. Luokan <em>render<\/em> -funktio m\u00e4\u00e4ritt\u00e4\u00e4, mit\u00e4 komponentteja ja muuta sis\u00e4lt\u00f6\u00e4 n\u00e4yt\u00f6lle halutaan.<\/p>\n<p><strong>React -projektin kehitt\u00e4minen create-react-app -rungon p\u00e4\u00e4lle<\/strong><\/p>\n<p>Kuten <a href=\"https:\/\/www.pilvikoodari.net\/?p=363\">edellisess\u00e4 blogipostauksessa<\/a> todettiin, React -ohjelman suunnittelu l\u00e4htee komponenttiajettelusta:\u00a0k\u00e4ytt\u00f6liittym\u00e4 pyrit\u00e4\u00e4n ajattelemaan komponenttien kautta ja sivulla n\u00e4kyv\u00e4t elementit toteutetaan erillisiin tiedostoihin jolloin syntyy helpommin ymm\u00e4rrett\u00e4v\u00e4mpi ohjelmarakenne,\u00a0kohtuullisen kokoisia ohjelmatiedostoja ja kaiken lis\u00e4ksi komponentteja\u00a0joita\u00a0voidaan hyv\u00e4ss\u00e4 tapauksessa uudelleenk\u00e4ytt\u00e4\u00e4 useassa paikassa ohjelmaa.<\/p>\n<p>Suunnittelemme seuraavaksi pienen ohjelman, jossa on muutama komponentti. N\u00e4in voimme kokeilla ohjelman jakamista komponentteihin ja n\u00e4emme miten React-j\u00e4rjestelm\u00e4n kehitt\u00e4minen k\u00e4ynnistyy.<\/p>\n<p>Tavoite: haluamme tehd\u00e4 Reactilla sovelluksen, miss\u00e4 k\u00e4ytt\u00e4j\u00e4 voi katsella Tampereen alueen tieliikennekameroiden reaaliaikaisia kuvia. Tarvitsemme vain n\u00e4yt\u00f6n, jossa k\u00e4ytt\u00e4j\u00e4 valitsee alasvetolistasta jonkin Tampereen ymp\u00e4rist\u00f6ss\u00e4 olevan kelikameran nimen ja sen j\u00e4lkeen alasvetolistan alapuolella n\u00e4ytet\u00e4\u00e4n kelikameran kuva.<\/p>\n<p>Suunnitelma: toteutetaan yksi &#8221;sivu&#8221; (= React- komponetti), johon asetellaan kaksi komponenttia: kameran valintakomponentti <em>CameraSelection<\/em> sek\u00e4 valitun kameran kuvaa n\u00e4ytt\u00e4v\u00e4 komponentti <em>CameraView<\/em>. P\u00e4\u00e4komponentilla eli &#8221;sivulla&#8221; on hallussaan tila eli <em>state, <\/em>jossa on tallessa valittu kamera. Kun k\u00e4ytt\u00e4j\u00e4 valitsee n\u00e4ytett\u00e4v\u00e4n kameran valintakomponentilla, valintakomponentti v\u00e4litt\u00e4\u00e4 valinnan p\u00e4\u00e4komponentille ja se puolestaan v\u00e4litt\u00e4\u00e4 uuden tiedon kuvaa n\u00e4ytt\u00e4v\u00e4lle komponentille.<\/p>\n<p>Ei muuta kuin koodaamaan!<\/p>\n<p>Perustetaan ensin src -hakemistoon kaksi hakemistoa komponenteille: <em>CameraSelection<\/em> ja <em>CameraView<\/em>.<\/p>\n<p>Toteutetaan ensin kameran valintakomponentti:<\/p>\n<pre class=\"lang:js decode:true\" title=\"CameraSelection.js\">import React, { Component } from 'react';\r\nimport PropTypes from 'prop-types';\r\nimport '.\/camera-selection.css';\r\n\r\n\/\/ http:\/\/tie.digitraffic.fi\/api\/v1\/metadata\/camera-stations\r\n\/\/ http:\/\/tie.digitraffic.fi\/api\/v1\/data\/camera-data\/{id}\r\nconst cameras = [\r\n    { presetId: '', name: '-' },\r\n    { presetId: 'C0454801', name: 'Lielahti, Yl\u00f6j\u00e4rvelle p\u00e4in' },\r\n    { presetId: 'C0454802', name: 'Lielahti, Tampereelle p\u00e4in' },\r\n    { presetId: 'C0460900', name: 'Rantatunnelin suu, it\u00e4' },\r\n    { presetId: 'C1850301', name: 'Hervannan valtav\u00e4, Hervantaan p\u00e4in' },\r\n    { presetId: 'C1850302', name: 'Hervannan valtav\u00e4, Keskustaan\/Kalevaan p\u00e4in' },\r\n    { presetId: 'C0450702', name: 'Lakalaiva, Helsinkiin p\u00e4in' },\r\n    { presetId: 'C0450701', name: 'Lakalaiva, Vaasaan p\u00e4in' }\r\n]\r\n\r\nclass CameraSelection extends Component {\r\n\r\n    selectionChanged = (event) =&gt; {\r\n        console.log(\"Selected:\" + event.target.value);\r\n        this.props.onSelect(event.target.value);\r\n    }\r\n\r\n    render = () =&gt; {\r\n        const { selectedPresetId } = this.props;\r\n        return &lt;div className=\"Camera-selection-container\"&gt;\r\n            &lt;div className=\"Camera-selection-text\"&gt;\r\n                Select camera to show:&lt;br \/&gt;\r\n                &lt;form&gt;\r\n                    &lt;select onChange={this.selectionChanged} value={selectedPresetId}&gt;\r\n                        {\r\n                            cameras.map((cam, i) =&gt;\r\n                                &lt;option\r\n                                    key={'selection_' + i}\r\n                                    value={cam.presetId}&gt;\r\n                                    {cam.name}\r\n                                &lt;\/option&gt;)\r\n                        }\r\n                    &lt;\/select&gt;\r\n                &lt;\/form&gt;\r\n            &lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n    }\r\n}\r\n\r\nCameraSelection.propTypes = {\r\n    selectedPresetId: PropTypes.string.isRequired,\r\n    onSelect: PropTypes.func.isRequired\r\n}\r\n\r\nexport default CameraSelection;<\/pre>\n<pre class=\"lang:css decode:true \" title=\"CameraSelection.css\">.Camera-selection-container {\r\n  align-content: center;\r\n  background-color: lightblue;\r\n  padding-top: 10px;\r\n  padding-bottom: 10px;\r\n}\r\n\r\n.Camera-selection-text {\r\n    font-size: 16;\r\n    color: 'blue';\r\n}<\/pre>\n<p><em>CameraSelection<\/em> -komponentille annetaan kaksi property\u00e4: <em>selectedPresetId<\/em> ja <em>onSelect<\/em>. N\u00e4ist\u00e4 ensimm\u00e4inen kertoo komponentille, mik\u00e4 valinta sen kuuluu n\u00e4ytt\u00e4\u00e4 aktiivisena. Toinen &#8221;propsi&#8221; taas on valinnan k\u00e4sittelev\u00e4 &#8221;callback&#8221; funktio. Komponentissa on kovakoodattuna taulukkoon muutama kamera Tampereen seudulta. Oikeassa projektissa kameralistaus voitaisiin hakea Liikenneviraston REST-rajapinnasta mutta j\u00e4tet\u00e4\u00e4n se harjoitusteht\u00e4v\u00e4ksi tai toiseen blogipostaukseen.<\/p>\n<p>Seuraavaksi voisimme lis\u00e4t\u00e4 t\u00e4m\u00e4n komponentin k\u00e4ytt\u00f6\u00f6n ohjelman &#8221;p\u00e4\u00e4sivulle&#8221; eli<em> App.js<\/em>\u00a0-tiedostoon:<\/p>\n<pre class=\"lang:js mark:4,24-27 decode:true \" title=\"App.js - kameran valinta lis\u00e4tty\">import React, { Component } from 'react';\r\nimport PropTypes from 'prop-types';\r\nimport '.\/App.css';\r\nimport CameraSelection from '.\/CameraSelection\/camera-selection'\r\n\r\nclass App extends Component {\r\n\r\n  constructor() {\r\n    super();\r\n    this.state = { selectedPresetId: null }\r\n  }\r\n\r\n  cameraSelected = (value) =&gt; {\r\n    this.setState({ selectedPresetId: value });\r\n  }\r\n\r\n  render = () =&gt; {\r\n    const { selectedPresetId } = this.state;\r\n    return (\r\n      &lt;div className=\"app\"&gt;\r\n        &lt;div className=\"app-header\"&gt;\r\n          &lt;h2&gt;Traffic Camera Viewer - Tampere&lt;\/h2&gt;\r\n        &lt;\/div&gt;\r\n        &lt;CameraSelection\r\n          selectedPresetId={this.state.selectedPresetId}\r\n          onSelect={(value) =&gt; this.cameraSelected(value)}\r\n        \/&gt;\r\n      &lt;\/div&gt;\r\n    );\r\n  }\r\n}\r\n\r\nApp.propTypes = {\r\n    selectedPresetId: PropTypes.string.isRequired\r\n}\r\n\r\nexport default App;\r\n<\/pre>\n<p>App.js -tiedostostossa n\u00e4kyy uusi komponentti korostettuna. Huomaa my\u00f6s komponentin tila eli <em>state<\/em>, jossa on tallessa nykyinen valinta.<\/p>\n<p>Todetaan ett\u00e4 valintakomponentti n\u00e4kyy sivulla:<\/p>\n<p><a href=\"https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-08-30-at-17.53.11.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-478\" src=\"https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-08-30-at-17.53.11.png\" alt=\"\" width=\"560\" height=\"446\" srcset=\"https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-08-30-at-17.53.11.png 560w, https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-08-30-at-17.53.11-300x239.png 300w\" sizes=\"auto, (max-width: 560px) 100vw, 560px\" \/><\/a>T\u00e4m\u00e4n j\u00e4lkeen toteutetaan viel\u00e4 kamerakuvan n\u00e4ytt\u00e4v\u00e4 komponentti:<\/p>\n<pre class=\"lang:js decode:true \" title=\"CameraView.js\">import React, { Component } from 'react';\r\nimport PropTypes from 'prop-types';\r\n\r\nclass CameraView extends Component {\r\n    render = () =&gt; {\r\n        const { presetId } = this.props;\r\n        return &lt;div className=\"Camera-selection-container\"&gt;\r\n            &lt;img\r\n                src={ `http:\/\/weathercam.digitraffic.fi\/${presetId}.jpg`}\r\n                alt={ `Camera ${presetId}`}\r\n             \/&gt;\r\n        &lt;\/div&gt;\r\n    }\r\n}\r\n\r\nCameraView.propTypes = {\r\n    presetId: PropTypes.string.isRequired\r\n}\r\n\r\nexport default CameraView;<\/pre>\n<p>T\u00e4m\u00e4 komponentti saa propsina vain halutun presetId:n, jonka perusteella se n\u00e4ytt\u00e4\u00e4 tieliikennekameran kuvan Liikenneviraston palvelusta.<\/p>\n<p>Otetaan CameraView k\u00e4ytt\u00f6\u00f6n <em>App.js<\/em> tiedostossa:<\/p>\n<pre class=\"lang:default mark:5,29-34 decode:true \" title=\"App.js\">import React, { Component } from 'react';\r\nimport PropTypes from 'prop-types';\r\nimport '.\/App.css';\r\nimport CameraSelection from '.\/CameraSelection\/camera-selection'\r\nimport CameraView from '.\/CameraView\/camera-view'\r\n\r\nclass App extends Component {\r\n\r\n  constructor() {\r\n    super();\r\n    this.state = { selectedPresetId: null }\r\n  }\r\n\r\n  cameraSelected = (value) =&gt; {\r\n    this.setState({ selectedPresetId: value });\r\n  }\r\n\r\n  render = () =&gt; {\r\n    const { selectedPresetId } = this.state;\r\n    return (\r\n      &lt;div className=\"app\"&gt;\r\n        &lt;div className=\"app-header\"&gt;\r\n          &lt;h2&gt;Traffic Camera Viewer - Tampere&lt;\/h2&gt;\r\n        &lt;\/div&gt;\r\n        &lt;CameraSelection\r\n          selectedPresetId={this.state.selectedPresetId}\r\n          onSelect={(value) =&gt; this.cameraSelected(value)}\r\n        \/&gt;\r\n        {selectedPresetId ?\r\n          &lt;div className=\"camera-container\"&gt;\r\n            &lt;CameraView presetId={selectedPresetId} \/&gt;\r\n          &lt;\/div&gt;\r\n          : null\r\n        }\r\n      &lt;\/div&gt;\r\n    );\r\n  }\r\n}\r\n\r\nApp.propTypes = {\r\n  selectedPresetId: PropTypes.string.isRequired\r\n}\r\n\r\nexport default App;<\/pre>\n<p>Projektimme on valmis! Testataan toimintaa valitsemalla Rantatunnelin kamera:<\/p>\n<p><a href=\"https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-08-30-at-18.04.04.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-480\" src=\"https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-08-30-at-18.04.04.png\" alt=\"\" width=\"888\" height=\"951\" srcset=\"https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-08-30-at-18.04.04.png 888w, https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-08-30-at-18.04.04-280x300.png 280w, https:\/\/www.pilvikoodari.net\/wp-content\/uploads\/2017\/06\/Screen-Shot-2017-08-30-at-18.04.04-768x822.png 768w\" sizes=\"auto, (max-width: 888px) 100vw, 888px\" \/><\/a><\/p>\n<p><strong>Yhteenveto<\/strong><\/p>\n<p><em>create-react-app<\/em> tekee React -projektin aloittamisen helpoksi. Se ottaa mukaan perus-riippuvuudet ja tarjoaa kehitysty\u00f6lle hyv\u00e4n perustan. Sen p\u00e4\u00e4lle on hyv\u00e4 jatkaa omaa kehitysty\u00f6t\u00e4 ja toteuttaa mit\u00e4 hienoimpia web-palveluita. React -ohjelman koon ja vaatimusten kasvaessa create-react-app -pohja ei kuitenkaan riit\u00e4 kovin pitk\u00e4lle, mutta pohjan p\u00e4\u00e4lle on helppo ottaa k\u00e4ytt\u00f6\u00f6n esim. tilanhallintakomponentti <a href=\"http:\/\/redux.js.org\/\">Redux<\/a>. Siit\u00e4 ja muitakin Reactin kehitysty\u00f6ss\u00e4 k\u00e4ytett\u00e4vist\u00e4 ty\u00f6kaluista voidaankin sitten puhua omassa blogipostauksessaan.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>React esittely -blogikirjoituksessa esiteltiin Reactin perusteita yksinkertaisella toteutuksella, jossa React ladattiin k\u00e4ytt\u00f6\u00f6n ns. CDN -latauksella. HTML-sivun l\u00e4hdekoodiin koodattiin suoraan Javascripti\u00e4 joka k\u00e4ytti React -kirjastoa. T\u00e4m\u00e4 tapa sopii keve\u00e4\u00e4n Reactin esittelyyn, mutta v\u00e4h\u00e4nk\u00e4\u00e4n vakavammassa projektissa t\u00e4m\u00e4 ei ole ollenkaan riitt\u00e4v\u00e4 tapa\u00a0k\u00e4ytt\u00e4\u00e4 Reactia. T\u00e4ss\u00e4 blogikirjoituksessa perustamme oikean React -kehitysymp\u00e4rist\u00f6n, jossa kehitysty\u00f6 onnistuu helposti ja tehokkaasti.\u00a0Koodausty\u00f6 tapahtuu JavaScript &#8230; <a title=\"React -projektin perustaminen\" class=\"read-more\" href=\"https:\/\/www.pilvikoodari.net\/?p=344\" aria-label=\"Lue lis\u00e4\u00e4 aiheesta React -projektin perustaminen\">Lue lis\u00e4\u00e4<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[14,7,12],"tags":[],"class_list":["post-344","post","type-post","status-publish","format-standard","hentry","category-react","category-tyokalujen-asennus","category-tyokalut"],"_links":{"self":[{"href":"https:\/\/www.pilvikoodari.net\/index.php?rest_route=\/wp\/v2\/posts\/344","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.pilvikoodari.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.pilvikoodari.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.pilvikoodari.net\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.pilvikoodari.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=344"}],"version-history":[{"count":39,"href":"https:\/\/www.pilvikoodari.net\/index.php?rest_route=\/wp\/v2\/posts\/344\/revisions"}],"predecessor-version":[{"id":483,"href":"https:\/\/www.pilvikoodari.net\/index.php?rest_route=\/wp\/v2\/posts\/344\/revisions\/483"}],"wp:attachment":[{"href":"https:\/\/www.pilvikoodari.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=344"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.pilvikoodari.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=344"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.pilvikoodari.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=344"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}