Skip to content
This repository was archived by the owner on Feb 12, 2024. It is now read-only.

Commit dc041aa

Browse files
kvutienkvutienlidel
authored
docs: examples/browser-create-react-app (#3694)
Co-authored-by: kvutien <[email protected]> Co-authored-by: Marcin Rataj <[email protected]>
1 parent cd548e6 commit dc041aa

File tree

6 files changed

+111
-118
lines changed

6 files changed

+111
-118
lines changed

examples/browser-create-react-app/README.md

+28-47
Original file line numberDiff line numberDiff line change
@@ -2,86 +2,67 @@
22

33
A minimal demonstration of how to use js-ipfs in a `create-react-app` generated app.
44

5-
It boots up a js-ipfs instance via a custom React hook in `./src/hooks/use-ipfs-factory.js`, which is called from `./src/App.js`, which is where the magic happens.
5+
It boots up a js-ipfs instance (an IPFS node) via a custom React hook in `./src/hooks/use-ipfs-factory.js`, which is called from `./src/App.js`. Once the IPFS node is set up, `App.js` displays its ident and its version number.
66

7-
![Screen shot of the js ipfs node id info](./screenshot.png)
7+
> _Remember that a Peer ID of an IPFS node is [the multihash of the public key of this node](https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md#peer-ids), and the public-private key pair of a node is generated by typing `ipfs init`._
88
9-
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). **v2.1.3**
9+
![Screen shot of the js ipfs node id info](./images/screenshot.png)
1010

11-
## Before you start
1211

13-
First clone this repo, install dependencies in the project root and build the project.
12+
**Note**: this example is useful to learn how to spawn IPFS from a web page. It is also possible to [spawn an IPFS daemon from the command line](https://docs.ipfs.io/install/command-line/) with `ipfs daemon`. While self-hosting is advised, one can also delegate IPFS operations to a third-party like Infura. See tutorials [here](https://blog.infura.io/part-2-getting-started-with-ipfs-on-infura/) and [here](https://blog.infura.io/part-2-getting-started-with-ipfs-on-infura/).
13+
14+
## To start
15+
16+
First clone the whole repo, install dependencies limited to this project `browser-create-react-app` and run the demo.
1417

1518
```console
1619
$ git clone https://github.com/ipfs/js-ipfs.git
17-
$ cd js-ipfs
20+
$ cd js-ipfs/examples/browser-create-react-app
1821
$ npm install
19-
$ npm run build
22+
$ npm start
2023
```
24+
## Call structure in `App.js`
25+
All React applications store their main logic in `App.js`:
26+
* `App.js` renders the cosmetics of the demo and call `useIpfs` to retrieve the `id` of the node
27+
* `useIpfsFactory.js` initialises and closes the IPFS local node
28+
* `useIpfs.js` does the actual calls to IPFS to retrieve the property specified in argument (here the retrieved property is `id`, requested from `App.js`)
29+
30+
## Annexes
31+
### Console message `[HMR] Waiting for update signal from WDS...`
2132

22-
## Available Scripts
33+
This message comes from the hot reload capability of webpack, that can update the web app every time you save your development code. To remove it, see here: https://stackoverflow.com/questions/59695102/reactjs-console-error-hmr-waiting-for-update-signal-from-wds
34+
35+
### Available Scripts from create-react-app
2336

2437
In the project directory, you can run:
2538

26-
### `npm start`
39+
#### `npm start`
2740

2841
Runs the app in the development mode.<br>
2942
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
3043

3144
The page will reload if you make edits.<br>
3245
You will also see any lint errors in the console.
3346

34-
### `npm test`
47+
#### `npm test`
3548

3649
Launches the test runner in the interactive watch mode.<br>
3750
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
3851

39-
### `npm run build`
52+
#### `npm run build`
4053

4154
Builds the app for production to the `build` folder.<br>
4255
It correctly bundles React in production mode and optimizes the build for the best performance.
4356

4457
The build is minified and the filenames include the hashes.<br>
45-
Your app is ready to be deployed!
46-
47-
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
58+
Your app is ready to be deployed! Read how to host a [single page](https://docs.ipfs.io/how-to/websites-on-ipfs/single-page-website/) or an [entire website](https://docs.ipfs.io/how-to/websites-on-ipfs/multipage-website/#prerequisites) on IPFS.
4859

49-
### `npm run eject`
60+
But with modern hosting services like Heroku, Netlity or Fleek, you can skip the build because they will do a complete github deployment for you. See the React official page about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
5061

51-
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
52-
53-
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
54-
55-
Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
56-
57-
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
5862

5963
## Learn More
6064

61-
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
62-
63-
To learn React, check out the [React documentation](https://reactjs.org/).
64-
65-
### Code Splitting
66-
67-
This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
68-
69-
### Analyzing the Bundle Size
70-
71-
This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
72-
73-
### Making a Progressive Web App
74-
75-
This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
76-
77-
### Advanced Configuration
78-
79-
This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
80-
81-
### Deployment
82-
83-
This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
65+
You can learn more on IPFS API in the [IPFS documentation](https://docs.ipfs.io/) and [IPFS npm documentation](https://www.npmjs.com/package/ipfs-http-client).
8466

85-
### `npm run build` fails to minify
67+
Details how to use the File System abstraction of IPFS (add, cat, egt, ls etc.) are [here](https://github.com/ipfs/js-ipfs/blob/master/docs/core-api/FILES.md)
8668

87-
This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify

examples/browser-create-react-app/src/App.js

+28-23
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@ import useIpfs from './hooks/use-ipfs.js'
44
import logo from './ipfs-logo.svg'
55

66
const App = () => {
7-
const { ipfs, ipfsInitError } = useIpfsFactory({ commands: ['id'] })
8-
const id = useIpfs(ipfs, 'id')
7+
// there is no change when we comment out the args of useIpfsFactory -> remove args?
8+
const { ipfs, ipfsInitError } = useIpfsFactory(/*{ commands: ['id'] }*/)
9+
// retrieve id of the current peer (see https://github.com/ipfs/js-ipfs/blob/master/docs/core-api/MISCELLANEOUS.md)
10+
const id = useIpfs(ipfs, 'id')
11+
912
return (
1013
<div className='sans-serif'>
1114
<header className='flex items-center pa3 bg-navy bb bw3 b--aqua'>
@@ -26,27 +29,29 @@ const App = () => {
2629
)
2730
}
2831

29-
const Title = ({ children }) => {
30-
return (
31-
<h2 className='f5 ma0 pb2 tracked aqua fw4 montserrat'>{children}</h2>
32-
)
33-
}
34-
3532
const IpfsId = (props) => {
36-
if (!props) return null
37-
return (
38-
<section className='bg-snow mw7 center mt5'>
39-
<h1 className='f3 fw4 ma0 pv3 aqua montserrat tc' data-test='title'>Connected to IPFS</h1>
40-
<div className='pa4'>
41-
{['id', 'agentVersion'].map((key) => (
42-
<div className='mb4' key={key}>
43-
<Title>{key}</Title>
44-
<div className='bg-white pa2 br2 truncate monospace' data-test={key}>{props[key]}</div>
45-
</div>
46-
))}
47-
</div>
48-
</section>
49-
)
33+
// the props of this component are all key-value pairs of the object 'id'
34+
// prints out the values of the keys 'id' and 'agentVersion' of the object 'id'
35+
if (!props) return null
36+
return (
37+
<section className='bg-snow mw7 center mt5'>
38+
<h1 className='f3 fw4 ma0 pv3 aqua montserrat tc' data-test='title'>Connected – properties of current IPFS node</h1>
39+
<div className='pa4'>
40+
{['id', 'agentVersion'].map((key) => (
41+
<div className='mb4' key={key}>
42+
<Title>{key}</Title>
43+
<div className='bg-white pa2 br2 truncate monospace' data-test={key}>{props[key]}</div>
44+
</div>
45+
))}
46+
</div>
47+
</section>
48+
)
5049
}
5150

52-
export default App
51+
const Title = ({ children }) => {
52+
return (
53+
<h2 className='f5 ma0 pb2 tracked aqua fw4 montserrat'>{children}</h2>
54+
)
55+
}
56+
57+
export default App
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import Ipfs from 'ipfs'
2+
// ipfs is the core API, a CLI and a HTTP server that functions as a HTTP to IPFS bridge
3+
// and an RPC endpoint. See https://www.npmjs.com/package/ipfs
24
import { useEffect, useState } from 'react'
35

46
let ipfs = null
@@ -14,44 +16,45 @@ let ipfs = null
1416
* it to be passed in.
1517
*/
1618
export default function useIpfsFactory () {
17-
const [isIpfsReady, setIpfsReady] = useState(Boolean(ipfs))
18-
const [ipfsInitError, setIpfsInitError] = useState(null)
19+
// initialise state variables, React hooks
20+
const [isIpfsReady, setIpfsReady] = useState(Boolean(ipfs))
21+
const [ipfsInitError, setIpfsInitError] = useState(null)
1922

20-
useEffect(() => {
21-
// The fn to useEffect should not return anything other than a cleanup fn,
22-
// So it cannot be marked async, which causes it to return a promise,
23-
// Hence we delegate to a async fn rather than making the param an async fn.
24-
async function startIpfs () {
25-
if (ipfs) {
26-
console.log('IPFS already started')
27-
} else if (window.ipfs && window.ipfs.enable) {
28-
console.log('Found window.ipfs')
29-
ipfs = await window.ipfs.enable({ commands: ['id'] })
30-
} else {
31-
try {
32-
console.time('IPFS Started')
33-
ipfs = await Ipfs.create()
34-
console.timeEnd('IPFS Started')
35-
} catch (error) {
36-
console.error('IPFS init error:', error)
37-
ipfs = null
38-
setIpfsInitError(error)
23+
useEffect(() => {
24+
// useEffect -as used here- is equivalent to componentDidMount in old React
25+
// The hook useEffect should not return anything other than a cleanup fn,
26+
// in addition, in a true life application there are many other context init things
27+
// hence in this example we make only a call to an async that initialises IPFS
28+
startIpfs()
29+
// ... add here any other init fn as required by an application
30+
return function cleanup () {
31+
if (ipfs && ipfs.stop) {
32+
console.log('Stopping IPFS')
33+
ipfs.stop().catch(err => console.error(err))
34+
ipfs = null
35+
setIpfsReady(false)
36+
}
3937
}
40-
}
38+
}, [])
4139

42-
setIpfsReady(Boolean(ipfs))
43-
}
40+
async function startIpfs () {
41+
// initialise IPFS daemon
42+
if (ipfs) {
43+
console.log('IPFS already started')
44+
} else {
45+
try {
46+
console.time('IPFS Started') // start timer
47+
ipfs = await Ipfs.create()
48+
console.timeEnd('IPFS Started') // stop timer and log duration in console
49+
} catch (error) {
50+
console.error('IPFS init error:', error)
51+
ipfs = null
52+
setIpfsInitError(error)
53+
}
54+
}
4455

45-
startIpfs()
46-
return function cleanup () {
47-
if (ipfs && ipfs.stop) {
48-
console.log('Stopping IPFS')
49-
ipfs.stop().catch(err => console.error(err))
50-
ipfs = null
51-
setIpfsReady(false)
52-
}
56+
setIpfsReady(Boolean(ipfs))
5357
}
54-
}, [])
5558

56-
return { ipfs, isIpfsReady, ipfsInitError }
59+
return { ipfs, isIpfsReady, ipfsInitError }
5760
}
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,30 @@
11
import { useState, useEffect } from 'react'
22
import dotProp from 'dot-prop'
3+
// dot-prop: used to obtain a property of an object when the name of property is a string
4+
// here we get ipfs.id when calling dotProp.get(ipfs, cmd), with cmd = 'id'
5+
// and we get ipfs.hash when calling with cmd = 'hash' etc.
36

47
/*
58
* Pass the command you'd like to call on an ipfs instance.
69
*
7-
* Uses setState to capture the response, so your component
8-
* will re-render when the result turns up.
10+
* callIpfs uses setState write the response as a state variable, so that your component
11+
* will re-render when the result 'res' turns up from the call await ipfsCmd.
912
*
1013
*/
1114
export default function useIpfs (ipfs, cmd, opts) {
12-
const [res, setRes] = useState(null)
13-
useEffect(() => {
14-
callIpfs(ipfs, cmd, opts, setRes)
15-
}, [ipfs, cmd, opts])
16-
return res
15+
// note: opts is not used here and is not passed as args of the call from App.js
16+
const [res, setRes] = useState(null)
17+
useEffect(() => {
18+
callIpfs(ipfs, cmd, opts, setRes)
19+
}, [ipfs, cmd, opts])
20+
return res
1721
}
1822

1923
async function callIpfs (ipfs, cmd, opts, setRes) {
20-
if (!ipfs) return null
21-
console.log(`Call ipfs.${cmd}`)
22-
const ipfsCmd = dotProp.get(ipfs, cmd)
23-
const res = await ipfsCmd(opts)
24-
console.log(`Result ipfs.${cmd}`, res)
25-
setRes(res)
24+
if (!ipfs) return null
25+
console.log(`Call ipfs.${cmd}`)
26+
const ipfsCmd = dotProp.get(ipfs, cmd)
27+
const res = await ipfsCmd(opts)
28+
console.log(`Result ipfs.${cmd}`, res)
29+
setRes(res)
2630
}

examples/browser-create-react-app/test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ module.exports = {
77
browser
88
.url(process.env.IPFS_EXAMPLE_TEST_URL)
99
.waitForElementVisible('[data-test=title]')
10-
.assert.containsText('[data-test=title]', 'Connected to IPFS')
10+
.assert.containsText('[data-test=title]', 'Connected – properties of current IPFS node')
1111
.assert.elementPresent('[data-test=id')
1212
.assert.elementPresent('[data-test=agentVersion')
1313
.end()

0 commit comments

Comments
 (0)