From c502d40ccd13b572da27ba08d412b19b7f302e21 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 15 May 2020 08:10:04 +0200 Subject: [PATCH 01/23] Correct link to NLeSC guide --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d6e34f8..0c85fc0 100644 --- a/README.md +++ b/README.md @@ -947,7 +947,7 @@ To make writing a SPA easier, a number of frameworks have been developed. The mo - [Vue.js](https://vuejs.org/) - [Angular](https://angular.io/) -They have their strengths and weaknesses which are summarized in the [NLeSC guide](https://guide.esciencecenter.nl/best_practices/language_guides/JavaScript.html#frameworks). +They have their strengths and weaknesses which are summarized in the [NLeSC guide](https://guide.esciencecenter.nl/#/best_practices/language_guides/javascript?id=frameworks). From e31d40eec03094476ce1a592e48d78ef2bd56908 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 15 May 2020 08:53:45 +0200 Subject: [PATCH 02/23] React app using babel standalone --- README.md | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 91 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0c85fc0..1e30b45 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ - [JavaScript](#JavaScript) - [Accessing C++ function from JavaScript in web browser](#accessing-c-function-from-JavaScript-in-web-browser) - [Executing long running methods in JavaScript](#executing-long-running-methods-in-JavaScript) - - [Single page application](#single-page-application) + - [Single page web application](#single-page-web-application) - [Form](#form) - [Visualization](#visualization) @@ -937,7 +937,7 @@ python3 -m http.server 8000 Visit [http://localhost:8000/src/js/example-web-worker.html](http://localhost:8000/src/js/example-web-worker.html) to see the root answer. The root finding answer was calculated using the C++ algorithm compiled to a WebAssembly module, imported in a web worker (seperate thread), executed by JavaScript with mesages to/from the web worker and rendered on a HTML page. -### Single page application +## Single page web application In the [Web application](#web_application) chapter a whole new page was rendered by the server even for a small change. With the advent of more powerful JavaScript engines in browsers and JavaScript methods to fetch JSON documents from a web service, it is possible to render the page with JavaScript and fetch a small change from the web service and re-render a small part of the page with JavaScript. The application running in the browser is called a [single page application](https://en.wikipedia.org/wiki/Single-page_application) or SPA. @@ -951,9 +951,96 @@ They have their strengths and weaknesses which are summarized in the [NLeSC guid -For Bubble I picked React as it is light and functional, because I like the small api footprint and the functional programming paradigm. +For NewtonRaphson web application I picked React as it is light and functional, because I like the small api footprint and the functional programming paradigm. -In Bubble the C++ is compiled to a wasm file using bindings. When a calculation form is submitted in the React application a web worker is started that loads the wasm file, starts the calculation, posts progress and lastly posts the result. With this architecture the application only needs cheap static file hosting to host the html, js and wasm files. **The calculation will be done in the web browser on the end users machine instead of a server**. +The C++ algorithm is compiled to a wasm file using bindings. When a calculation form is submitted in the React application a web worker is started that loads the wasm file, starts the calculation, posts progress and lastly posts the result. With this architecture the application only needs cheap static file hosting to host the html, js and wasm files. **The calculation will be done in the web browser on the end users machine instead of a server**. + +### React component + +```{.jsx file=src/js/app.js} +function Heading() { + return

Root finding web application

+} + +function App() { + const [epsilon, setEpsilon] = React.useState(0.001); + const [guess, setGuess] = React.useState(-20); + const [root, setRoot] = React.useState(undefined); + + function onEpsilonChange(event) { + setEpsilon(event.target.value); + } + + function onGuessChange(event) { + setGuess(event.target.value); + } + + function handleSubmit(event) { + event.preventDefault(); + + const worker = new Worker('worker.js'); + + worker.onmessage = function(message) { + if (message.data.type === 'RESULT') { + const root = message.data.data.root; + setRoot(root); + worker.terminate(); + } + }; + + worker.postMessage({ + type: 'CALCULATE', + data: { epsilon: 0.001, guess: -20 } + }); + } + + return ( +
+ +
+ + + +
+ +
+ ); +} + +function Result(props) { + const root = props.root; + let message = 'Not submitted'; + if (root !== undefined) { + message = 'Root = ' + root; + } + return
{message}
; +} + +ReactDOM.render( + , + document.getElementById('root') +); +``` + +```{.html file=src/js/app.html} + + + + + + + +
+ + + +``` ### Form From 55b67b310d1092a6e42067dfce62fe7bfa45aa53 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 15 May 2020 12:36:30 +0200 Subject: [PATCH 03/23] Added React chapter Fixes #14 --- INSTALL.md | 17 ++++ README.md | 234 ++++++++++++++++++++++++++++++++++++++++------------- index.html | 1 + 3 files changed, 196 insertions(+), 56 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 6b31d73..58c0569 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -118,6 +118,8 @@ To test the WebAssembly module we will use the [cypress](https://www.cypress.io/ We want to test if visiting the example web page renders the answer `-1.00`. +First a test for the direct WebAssembly example. + ```{.js file=cypress/integration/example_spec.js} describe('src/js/example.html', () => { it('should render -1.00', () => { @@ -127,6 +129,8 @@ describe('src/js/example.html', () => { }); ``` +Second a test for the WebAssembly called through a web worker. + ```{.js file=cypress/integration/example-web-worker_spec.js} describe('src/js/example-web-worker.html', () => { it('should render -1.00', () => { @@ -136,6 +140,19 @@ describe('src/js/example-web-worker.html', () => { }); ``` +And lastly a test for the full React/form/Web worker/WebAssembly combination. + +```{.js file=cypress/integration/example-app_spec.js} +describe('src/js/example-app.html', () => { + it('should render -1.00', () => { + cy.visit('http://localhost:8000/src/js/example-app.html'); + cy.get('input[name=guess]').type('-30'); + cy.contains('Submit').click(); + cy.get('#answer').contains('-1.00'); + }); +}); +``` + The test can be run with ```{.awk #test-wasm} diff --git a/README.md b/README.md index 1e30b45..d6d4429 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ - [Accessing C++ function from JavaScript in web browser](#accessing-c-function-from-JavaScript-in-web-browser) - [Executing long running methods in JavaScript](#executing-long-running-methods-in-JavaScript) - [Single page web application](#single-page-web-application) + - [React component](#react-component) - [Form](#form) - [Visualization](#visualization) @@ -957,92 +958,213 @@ The C++ algorithm is compiled to a wasm file using bindings. When a calculation ### React component +To render the React application we need a HTML tag as a container. We will give it the identifier `root` which will use later when +we implement the React application in the `app.js` file. + +```{.html file=src/js/example-app.html} + + + + <> +
+ + + +``` + +To use React we need to import the React library. + +```{.html #imports} + + +``` + +A React application is constructed from React components. The simplest React component is a function which returns something which looks like a HTML snippet. + ```{.jsx file=src/js/app.js} function Heading() { - return

Root finding web application

+ const title = 'Root finding web application'; + return

{title}

} +``` -function App() { - const [epsilon, setEpsilon] = React.useState(0.001); - const [guess, setGuess] = React.useState(-20); - const [root, setRoot] = React.useState(undefined); +A component can be rendered using + +```jsx +ReactDOM.render( + , + document.getElementById('root') +); +``` + +The `Heading` React component would render to the following HTML. + +```html +

Root finding web application

+``` + +The `

{title}

` looks like HTML, but is actually called [JSX](https://reactjs.org/docs/introducing-jsx.html). +A transformer like [Babel](https://babeljs.io/docs/en/next/babel-standalone.html) can convert JSX to valid JavaScript code. The transformed Heading component will look like. + +```js +function Heading() { + const title = 'Root finding web application'; + return React.createElement('h1', null, `{title}`); +} +``` + +JXS is syntactic sugar that makes React components easier to write and read. So will use JSX going forward. + +To transform JSX we need to import Babel. + +```{.html #imports} + +``` + +The code supplied here should not be used in production as converting JSX in the web browser is slow. Better to use [Create React App](http://create-react-app.dev/) which gives you an infrastructere to perform the transformation offline. + +The web application should have a form with a `epsilon` and `guess` input field and a submit button. +The form in JSX can be written in the following way + +```{.jsx #react-form} +
+ + + +
+``` + +The form tag has a `handleSubmit` variable, which is a function that will handle the form submission. +The input tag will render the `value` and when the user changes the value the `onChange` function will be called. + +Let's implement the value and onChange for the epsilon input. +To store the value will use the [React useState hook](https://reactjs.org/docs/hooks-state.html). + +```{.js #react-state} +const [epsilon, setEpsilon] = React.useState(0.001); +``` + +The argument of the `useState` function is the initial value. The `epsilon` variable contains the current value for epsilon and `setEpsilon` is a function to set epsilon to a new value. + +The input tag in the form will call the onChange function with a event object. We need to dig the user supplied new value out of the event and pass it to `setEpsilon`. + +```{.js #react-state} +function onEpsilonChange(event) { + setEpsilon(event.target.value); +} +``` + +The guess input will be given the same treatment. + +```{.js #react-state} +const [guess, setGuess] = React.useState(-20); + +function onGuessChange(event) { + setGuess(event.target.value); +} +``` + +We are ready to implement the `handleSubmit` function. +The function will get, similar to the onChange of the input tag, an event object. +Normally when you submit a form the form fields will be send to the server, but we want to perform the calculation in the browser so we have to disable the default action with. - function onEpsilonChange(event) { - setEpsilon(event.target.value); +```{.jsx #handle-submit} +event.preventDefault(); +``` + +Like we did in the previous chapter we have to construct a web worker. + +```{.jsx #handle-submit} +const worker = new Worker('worker.js'); +``` + +We have to post a message to the worker with the values from the form. + +```{.jsx #handle-submit} +worker.postMessage({ + type: 'CALCULATE', + data: { epsilon: epsilon, guess: guess } +}); +``` + +We need a place to store the resulting `root` value, we will use `useState` function again. +The initial value is set to `undefined` as the `root` value is only known after the calculation has been completed. + +```{.js #react-state} +const [root, setRoot] = React.useState(undefined); +``` + +When the worker is done it will send a message back to the app. The app needs to store the `root` result value. The worker can then be terminated because it did its job. + +```{.jsx #handle-submit} +worker.onmessage = function(message) { + if (message.data.type === 'RESULT') { + const root = message.data.data.root; + setRoot(root); + worker.terminate(); } +}; +``` + +To render the result we can use a React Component which has `root` as a property. +When the calculation has not be done yet will render `Not submitted`. +When the `root` value is set to we will show it. - function onGuessChange(event) { - setGuess(event.target.value); +```{.jsx file=src/js/app.js} +function Result(props) { + const root = props.root; + let message = 'Not submitted'; + if (root !== undefined) { + message = 'Root = ' + root; } + return
{message}
; +} +``` + +We can combine the heading, form and result components and all the states and handleSubmit function into the `App` React component. + +```{.jsx file=src/js/app.js} +function App() { + <> function handleSubmit(event) { - event.preventDefault(); - - const worker = new Worker('worker.js'); - - worker.onmessage = function(message) { - if (message.data.type === 'RESULT') { - const root = message.data.data.root; - setRoot(root); - worker.terminate(); - } - }; - - worker.postMessage({ - type: 'CALCULATE', - data: { epsilon: 0.001, guess: -20 } - }); + <> } return (
-
- - - -
+ <>
); } +``` -function Result(props) { - const root = props.root; - let message = 'Not submitted'; - if (root !== undefined) { - message = 'Root = ' + root; - } - return
{message}
; -} +Finally we can render the `App` component to the HTML container with `root` as identifier. +```{.jsx file=src/js/app.js} ReactDOM.render( , document.getElementById('root') ); ``` -```{.html file=src/js/app.html} - - - - - - - -
+Like before we also need to host the files in a web server with - - +```shell +python3 -m http.server 8000 ``` -### Form +Visit [http://localhost:8000/src/js/example-app.html](http://localhost:8000/src/js/example-app.html) to see the root answer. + +### JSON schema powered form The JSON schema can be used to generate a form. The form submission will be validated against the schema. The most popular JSON schema form for React is [react-jsonschema-form](https://github.com/rjsf-team/react-jsonschema-form). diff --git a/index.html b/index.html index 20fe462..ba02ecf 100644 --- a/index.html +++ b/index.html @@ -50,5 +50,6 @@ + From 26a0e30ebec941968c0fcedf9ea090cb27c2e3ac Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 15 May 2020 12:50:25 +0200 Subject: [PATCH 04/23] Englsih --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d6d4429..019ae30 100644 --- a/README.md +++ b/README.md @@ -1114,7 +1114,7 @@ worker.onmessage = function(message) { To render the result we can use a React Component which has `root` as a property. When the calculation has not be done yet will render `Not submitted`. -When the `root` value is set to we will show it. +When the `root` value is set then we will show it. ```{.jsx file=src/js/app.js} function Result(props) { From e424c37dd275973b9d47219a9f4439aa973e56b4 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 15 May 2020 15:55:02 +0200 Subject: [PATCH 05/23] Update README.md Co-authored-by: Faruk D. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4c87797..d3f0d1f 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ - [JavaScript](#JavaScript) - [Accessing C++ function from JavaScript in web browser](#accessing-c-function-from-JavaScript-in-web-browser) - [Executing long running methods in JavaScript](#executing-long-running-methods-in-JavaScript) - - [Single page web application](#single-page-web-application) + - [Single page application](#single-page-application) - [React component](#react-component) - [Form](#form) - [Visualization](#visualization) From e04c24d4fbe4755cc911b341cbcf74509eb004aa Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 15 May 2020 15:55:09 +0200 Subject: [PATCH 06/23] Update README.md Co-authored-by: Faruk D. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d3f0d1f..510915c 100644 --- a/README.md +++ b/README.md @@ -940,7 +940,7 @@ python3 -m http.server 8000 Visit [http://localhost:8000/src/js/example-web-worker.html](http://localhost:8000/src/js/example-web-worker.html) to see the result of the calculation. The result of root finding was calculated using the C++ algorithm compiled to a WebAssembly module, imported in a web worker (separate thread), executed by JavaScript with messages to/from the web worker and rendered on a HTML page. -## Single page web application +## Single page application In the [Web application](#web_application) chapter, a whole new page was rendered by the server even for a small change. With the advent of more powerful JavaScript engines in browsers and JavaScript methods to fetch JSON documents from a web service, it is possible to prevent that. [Single Page Applications](https://en.wikipedia.org/wiki/Single-page_application)(SPA) can render the page and fetch a small change from the web service and re-render a small part of the page with JavaScript. From 8790902eea7b7519b156a4dd920d6368914b22b2 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 15 May 2020 15:57:35 +0200 Subject: [PATCH 07/23] Update README.md Co-authored-by: Faruk D. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 510915c..f77a38e 100644 --- a/README.md +++ b/README.md @@ -950,7 +950,7 @@ To make writing a SPA easier, a number of frameworks have been developed. The mo - [Vue.js](https://vuejs.org/) - [Angular](https://angular.io/) -They have their strengths and weaknesses which are summarized in the [NLeSC guide](https://guide.esciencecenter.nl/#/best_practices/language_guides/javascript?id=frameworks). +They have their strengths and weaknesses which are summarized in the [here](https://en.wikipedia.org/wiki/Comparison_of_JavaScript_frameworks#Features). From f8a2faba43e4f3170397b8c19cab85d31a466038 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 15 May 2020 15:58:03 +0200 Subject: [PATCH 08/23] Update README.md Co-authored-by: Faruk D. --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index f77a38e..890e05f 100644 --- a/README.md +++ b/README.md @@ -952,7 +952,6 @@ To make writing a SPA easier, a number of frameworks have been developed. The mo They have their strengths and weaknesses which are summarized in the [here](https://en.wikipedia.org/wiki/Comparison_of_JavaScript_frameworks#Features). - For NewtonRaphson web application I picked React as it is light and functional, because I like the small api footprint and the functional programming paradigm. From 777d2d5e6b9ff776a4c3e2718f52895f0eca9a0b Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 15 May 2020 16:00:11 +0200 Subject: [PATCH 09/23] Update README.md Co-authored-by: Faruk D. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 890e05f..9158a05 100644 --- a/README.md +++ b/README.md @@ -953,7 +953,7 @@ To make writing a SPA easier, a number of frameworks have been developed. The mo They have their strengths and weaknesses which are summarized in the [here](https://en.wikipedia.org/wiki/Comparison_of_JavaScript_frameworks#Features). -For NewtonRaphson web application I picked React as it is light and functional, because I like the small api footprint and the functional programming paradigm. +For Newton-Raphson web application I picked React as it is light and functional, because I like the small API footprint and the functional programming paradigm. The C++ algorithm is compiled to a wasm file using bindings. When a calculation form is submitted in the React application a web worker is started that loads the wasm file, starts the calculation, posts progress and lastly posts the result. With this architecture the application only needs cheap static file hosting to host the html, js and wasm files. **The calculation will be done in the web browser on the end users machine instead of a server**. From b1dbc8740424fcff58d2d9b0d041e7ecb8255a27 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 15 May 2020 16:03:03 +0200 Subject: [PATCH 10/23] Update README.md Co-authored-by: Faruk D. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9158a05..06f3c08 100644 --- a/README.md +++ b/README.md @@ -955,7 +955,7 @@ They have their strengths and weaknesses which are summarized in the [here](http For Newton-Raphson web application I picked React as it is light and functional, because I like the small API footprint and the functional programming paradigm. -The C++ algorithm is compiled to a wasm file using bindings. When a calculation form is submitted in the React application a web worker is started that loads the wasm file, starts the calculation, posts progress and lastly posts the result. With this architecture the application only needs cheap static file hosting to host the html, js and wasm files. **The calculation will be done in the web browser on the end users machine instead of a server**. +The C++ algorithm is compiled into a wasm file using bindings. When a calculation form is submitted in the React application a web worker loads the wasm file, starts the calculation, renders the result. With this architecture the application only needs cheap static file hosting to host the html, js and wasm files. **The calculation will be done in the web browser on the end users machine instead of a server**. ### React component From 3f37388e5a6c7321394c37236bc4285268108051 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 15 May 2020 16:03:56 +0200 Subject: [PATCH 11/23] Use container as id --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4c87797..0ae17ce 100644 --- a/README.md +++ b/README.md @@ -960,7 +960,7 @@ The C++ algorithm is compiled to a wasm file using bindings. When a calculation ### React component -To render the React application we need a HTML tag as a container. We will give it the identifier `root` which will use later when +To render the React application we need a HTML tag as a container. We will give it the identifier `container` which will use later when we implement the React application in the `app.js` file. ```{.html file=src/js/example-app.html} @@ -968,7 +968,7 @@ we implement the React application in the `app.js` file. <> -
+
@@ -995,7 +995,7 @@ A component can be rendered using ```jsx ReactDOM.render( , - document.getElementById('root') + document.getElementById('container') ); ``` @@ -1149,12 +1149,12 @@ function App() { } ``` -Finally we can render the `App` component to the HTML container with `root` as identifier. +Finally we can render the `App` component to the HTML container with `container` as identifier. ```{.jsx file=src/js/app.js} ReactDOM.render( , - document.getElementById('root') + document.getElementById('container') ); ``` From 92fd9ea33b775dd025a26c61dd710ac27f0367bd Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 15 May 2020 16:07:11 +0200 Subject: [PATCH 12/23] Missing ; --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ec39de7..13e0496 100644 --- a/README.md +++ b/README.md @@ -1001,7 +1001,7 @@ ReactDOM.render( The `Heading` React component would render to the following HTML. ```html -

Root finding web application

+

Root finding web application

; ``` The `

{title}

` looks like HTML, but is actually called [JSX](https://reactjs.org/docs/introducing-jsx.html). From 1499f5ce29e5f6e0561a1964ac34310e20a78f69 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 15 May 2020 16:12:05 +0200 Subject: [PATCH 13/23] Update README.md Co-authored-by: Faruk D. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 13e0496..a93679c 100644 --- a/README.md +++ b/README.md @@ -1014,7 +1014,7 @@ function Heading() { } ``` -JXS is syntactic sugar that makes React components easier to write and read. So will use JSX going forward. +JXS is syntactic sugar that makes React components easier to write and read. In the rest of the chapter, we will use JSX. To transform JSX we need to import Babel. From 4a2bde0a332b8a189dc128ab90f28b0027d3e830 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 15 May 2020 16:14:19 +0200 Subject: [PATCH 14/23] Update README.md Co-authored-by: Faruk D. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a93679c..735ec98 100644 --- a/README.md +++ b/README.md @@ -980,7 +980,7 @@ To use React we need to import the React library. ``` -A React application is constructed from React components. The simplest React component is a function which returns something which looks like a HTML snippet. +A React application is constructed from React components. The simplest React component is a function which returns a HTML tag with a variable inside. ```{.jsx file=src/js/app.js} function Heading() { From ab70f0de7ecbaf1c36e29ed93a668c5fc880a342 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 15 May 2020 16:14:54 +0200 Subject: [PATCH 15/23] Update README.md Co-authored-by: Faruk D. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 735ec98..c9e9f31 100644 --- a/README.md +++ b/README.md @@ -1022,7 +1022,7 @@ To transform JSX we need to import Babel. ``` -The code supplied here should not be used in production as converting JSX in the web browser is slow. Better to use [Create React App](http://create-react-app.dev/) which gives you an infrastructere to perform the transformation offline. +The code supplied here should not be used in production as converting JSX in the web browser is slow. Better to use [Create React App](http://create-react-app.dev/) which gives you an infrastructure to perform the transformation offline. The web application should have a form with a `epsilon` and `guess` input field and a submit button. The form in JSX can be written in the following way From e022461b86b23318174ba6224e2dd0d0731bc9d8 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 15 May 2020 16:15:37 +0200 Subject: [PATCH 16/23] Update README.md Co-authored-by: Faruk D. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c9e9f31..6164d5c 100644 --- a/README.md +++ b/README.md @@ -1024,7 +1024,7 @@ To transform JSX we need to import Babel. The code supplied here should not be used in production as converting JSX in the web browser is slow. Better to use [Create React App](http://create-react-app.dev/) which gives you an infrastructure to perform the transformation offline. -The web application should have a form with a `epsilon` and `guess` input field and a submit button. +The web application in our example should have a form with a `epsilon` and `guess` input field and a submit button. The form in JSX can be written in the following way ```{.jsx #react-form} From 1e10db038de75f5a0d647bd1d99f614270a96e29 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 15 May 2020 16:15:56 +0200 Subject: [PATCH 17/23] Update README.md Co-authored-by: Faruk D. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6164d5c..efebfaf 100644 --- a/README.md +++ b/README.md @@ -1025,7 +1025,7 @@ To transform JSX we need to import Babel. The code supplied here should not be used in production as converting JSX in the web browser is slow. Better to use [Create React App](http://create-react-app.dev/) which gives you an infrastructure to perform the transformation offline. The web application in our example should have a form with a `epsilon` and `guess` input field and a submit button. -The form in JSX can be written in the following way +The form in JSX can be written in the following way: ```{.jsx #react-form}
From 399081eb1e261a0f1173c356d45aed988f9a8a03 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 15 May 2020 16:39:12 +0200 Subject: [PATCH 18/23] Get result from worker --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index efebfaf..669a884 100644 --- a/README.md +++ b/README.md @@ -1106,8 +1106,8 @@ When the worker is done it will send a message back to the app. The app needs to ```{.jsx #handle-submit} worker.onmessage = function(message) { if (message.data.type === 'RESULT') { - const root = message.data.data.root; - setRoot(root); + const result = message.data.payload.root; + setRoot(result); worker.terminate(); } }; From d4d4288dbbf258c5653c2dc9589058fc51e7e597 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 15 May 2020 16:49:10 +0200 Subject: [PATCH 19/23] Apply suggestions from code review Co-authored-by: Faruk D. --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 669a884..a787965 100644 --- a/README.md +++ b/README.md @@ -1041,11 +1041,11 @@ The form in JSX can be written in the following way: ``` -The form tag has a `handleSubmit` variable, which is a function that will handle the form submission. -The input tag will render the `value` and when the user changes the value the `onChange` function will be called. +The form tag has a `onSubmit` property, which is set to a function (`handleSubmit`) that will handle the form submission. +The input tag has a `value` property to set the variable (`epsilon` and `guess`) and it also has `onChange` property to set the function (`onEpsilonChange` and `onGuessChange`) which will be triggered when the user changes the value. -Let's implement the value and onChange for the epsilon input. -To store the value will use the [React useState hook](https://reactjs.org/docs/hooks-state.html). +Let's implement the `value` and `onChange` for the `epsilon` input. +To store the value we will use the [React useState hook](https://reactjs.org/docs/hooks-state.html). ```{.js #react-state} const [epsilon, setEpsilon] = React.useState(0.001); @@ -1053,7 +1053,7 @@ const [epsilon, setEpsilon] = React.useState(0.001); The argument of the `useState` function is the initial value. The `epsilon` variable contains the current value for epsilon and `setEpsilon` is a function to set epsilon to a new value. -The input tag in the form will call the onChange function with a event object. We need to dig the user supplied new value out of the event and pass it to `setEpsilon`. +The input tag in the form will call the `onChange` function with a event object. We need to extract the user input from the event and pass it to `setEpsilon`. ```{.js #react-state} function onEpsilonChange(event) { @@ -1061,7 +1061,7 @@ function onEpsilonChange(event) { } ``` -The guess input will be given the same treatment. +We will follow the same steps for the guess input as well. ```{.js #react-state} const [guess, setGuess] = React.useState(-20); @@ -1071,7 +1071,7 @@ function onGuessChange(event) { } ``` -We are ready to implement the `handleSubmit` function. +We are ready to implement the `handleSubmit` function which will process the form data. The function will get, similar to the onChange of the input tag, an event object. Normally when you submit a form the form fields will be send to the server, but we want to perform the calculation in the browser so we have to disable the default action with. @@ -1094,14 +1094,14 @@ worker.postMessage({ }); ``` -We need a place to store the resulting `root` value, we will use `useState` function again. -The initial value is set to `undefined` as the `root` value is only known after the calculation has been completed. +We need a place to store the result of the calculation (`root` value), we will use `useState` function again. +The initial value of the result is set to `undefined` as the result is only known after the calculation has been completed. ```{.js #react-state} const [root, setRoot] = React.useState(undefined); ``` -When the worker is done it will send a message back to the app. The app needs to store the `root` result value. The worker can then be terminated because it did its job. +When the worker is done it will send a message back to the app. The app needs to store the result value (`root`) using `setRoot`. The worker will then be terminated because it did its job. ```{.jsx #handle-submit} worker.onmessage = function(message) { @@ -1114,8 +1114,8 @@ worker.onmessage = function(message) { ``` To render the result we can use a React Component which has `root` as a property. -When the calculation has not be done yet will render `Not submitted`. -When the `root` value is set then we will show it. +When the calculation has not been done yet, it will render `Not submitted`. +When the `root` property value is set then we will show it. ```{.jsx file=src/js/app.js} function Result(props) { From e53c7991cd727339ab9b33416d545b3de82d75a5 Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 15 May 2020 16:51:51 +0200 Subject: [PATCH 20/23] Use payload instead of data --- Makefile | 44 ++++++++++++++++++++++---------------------- README.md | 2 +- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Makefile b/Makefile index 1a1fc32..0e7bac7 100644 --- a/Makefile +++ b/Makefile @@ -3,70 +3,70 @@ py-deps: pip-pybind11 pip-flask pip-celery pip-connexion pip-pybind11: - pip install pybind11 + pip install pybind11 pip-flask: - pip install flask + pip install flask pip-celery: - pip install celery[redis] + pip install celery[redis] pip-connexion: - pip install connexion[swagger-ui] + pip install connexion[swagger-ui] bin/newtonraphson.exe: src/cli-newtonraphson.cpp - g++ src/cli-newtonraphson.cpp -o bin/newtonraphson.exe + g++ src/cli-newtonraphson.cpp -o bin/newtonraphson.exe test-cli: bin/newtonraphson.exe - ./bin/newtonraphson.exe + ./bin/newtonraphson.exe apache2/cgi-bin/newtonraphson: src/cgi-newtonraphson.cpp - g++ -Ideps src/cgi-newtonraphson.cpp -o apache2/cgi-bin/newtonraphson + g++ -Ideps src/cgi-newtonraphson.cpp -o apache2/cgi-bin/newtonraphson test-cgi: apache2/cgi-bin/newtonraphson - echo '{"guess":-20, "epsilon":0.001}' | apache2/cgi-bin/newtonraphson + echo '{"guess":-20, "epsilon":0.001}' | apache2/cgi-bin/newtonraphson src/py/newtonraphsonpy.*.so: src/py-newtonraphson.cpp - g++ -O3 -Wall -shared -std=c++14 -fPIC `python3 -m pybind11 --includes` \ - src/py-newtonraphson.cpp -o src/py/newtonraphsonpy`python3-config --extension-suffix` + g++ -O3 -Wall -shared -std=c++14 -fPIC `python3 -m pybind11 --includes` \ + src/py-newtonraphson.cpp -o src/py/newtonraphsonpy`python3-config --extension-suffix` test-py: src/py/example.py src/py/newtonraphsonpy.*.so - python src/py/example.py + python src/py/example.py test: test-cli test-cgi test-py test-webservice # Removes the compiled files clean: - $(RM) bin/newtonraphson.exe src/py/newtonraphsonpy.*.so apache2/cgi-bin/newtonraphson src/js/newtonraphsonwasm.js src/js/newtonraphsonwasm.wasm + $(RM) bin/newtonraphson.exe src/py/newtonraphsonpy.*.so apache2/cgi-bin/newtonraphson src/js/newtonraphsonwasm.js src/js/newtonraphsonwasm.wasm start-redis: - docker run --rm -d -p 6379:6379 --name some-redis redis + docker run --rm -d -p 6379:6379 --name some-redis redis stop-redis: - docker stop some-redis + docker stop some-redis run-webapp: src/py/newtonraphsonpy.*.so - python src/py/webapp.py + python src/py/webapp.py run-webservice: src/py/newtonraphsonpy.*.so - python src/py/webservice.py + python src/py/webservice.py test-webservice: - curl -X POST "http://localhost:8080/api/newtonraphson" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"epsilon\":0.001,\"guess\":-20}" + curl -X POST "http://localhost:8080/api/newtonraphson" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"epsilon\":0.001,\"guess\":-20}" run-celery-worker: src/py/newtonraphsonpy.*.so - PYTHONPATH=src/py celery worker -A tasks + PYTHONPATH=src/py celery worker -A tasks run-celery-webapp: src/py/newtonraphsonpy.*.so - python src/py/webapp-celery.py + python src/py/webapp-celery.py build-wasm: src/js/newtonraphsonwasm.js src/js/newtonraphsonwasm.wasm src/js/newtonraphsonwasm.js src/js/newtonraphsonwasm.wasm: src/wasm-newtonraphson.cpp - emcc --bind -o src/js/newtonraphsonwasm.js -s MODULARIZE=1 -s EXPORT_NAME=createModule src/wasm-newtonraphson.cpp + emcc --bind -o src/js/newtonraphsonwasm.js -s MODULARIZE=1 -s EXPORT_NAME=createModule src/wasm-newtonraphson.cpp host-files: build-wasm - python3 -m http.server 8000 + python3 -m http.server 8000 test-wasm: - npx cypress run --config-file false \ No newline at end of file + npx cypress run --config-file false \ No newline at end of file diff --git a/README.md b/README.md index a787965..121cfb7 100644 --- a/README.md +++ b/README.md @@ -1090,7 +1090,7 @@ We have to post a message to the worker with the values from the form. ```{.jsx #handle-submit} worker.postMessage({ type: 'CALCULATE', - data: { epsilon: epsilon, guess: guess } + payload: { epsilon: epsilon, guess: guess } }); ``` From abee96583e90107a8b86197cdb4dd389258f45df Mon Sep 17 00:00:00 2001 From: Stefan Verhoeven Date: Fri, 15 May 2020 17:01:19 +0200 Subject: [PATCH 21/23] Added anchor comments to code-blocks --- README.md | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 121cfb7..8aeb9bd 100644 --- a/README.md +++ b/README.md @@ -976,13 +976,15 @@ we implement the React application in the `app.js` file. To use React we need to import the React library. ```{.html #imports} - - + + + ``` A React application is constructed from React components. The simplest React component is a function which returns a HTML tag with a variable inside. ```{.jsx file=src/js/app.js} +// this JavaScript snippet is stored as src/js/app.js function Heading() { const title = 'Root finding web application'; return

{title}

@@ -1019,7 +1021,8 @@ JXS is syntactic sugar that makes React components easier to write and read. In To transform JSX we need to import Babel. ```{.html #imports} - + + ``` The code supplied here should not be used in production as converting JSX in the web browser is slow. Better to use [Create React App](http://create-react-app.dev/) which gives you an infrastructure to perform the transformation offline. @@ -1028,6 +1031,7 @@ The web application in our example should have a form with a `epsilon` and `gues The form in JSX can be written in the following way: ```{.jsx #react-form} +// this JavaScript snippet is later referred to as <>