{"id":15725862,"url":"https://github.com/ndrean/navbar","last_synced_at":"2026-04-15T16:08:29.549Z","repository":{"id":124425898,"uuid":"321796762","full_name":"ndrean/navbar","owner":"ndrean","description":"Skeleton React app routed by Universal Router, state managed by Mobx, dynamic forms with R-Hook-Forms,, designed with Material-UI, with form and modal login and Facebook Login","archived":false,"fork":false,"pushed_at":"2020-12-29T15:12:15.000Z","size":19680,"stargazers_count":1,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-30T03:03:30.951Z","etag":null,"topics":["material-ui","mobx-react","react","react-hook-forms","universal-router"],"latest_commit_sha":null,"homepage":"","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/ndrean.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-12-15T21:46:04.000Z","updated_at":"2022-06-24T09:09:26.000Z","dependencies_parsed_at":null,"dependency_job_id":"2cc3db3e-cbe3-4e46-9f70-20aab4ab7c8b","html_url":"https://github.com/ndrean/navbar","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ndrean/navbar","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ndrean%2Fnavbar","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ndrean%2Fnavbar/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ndrean%2Fnavbar/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ndrean%2Fnavbar/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ndrean","download_url":"https://codeload.github.com/ndrean/navbar/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ndrean%2Fnavbar/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31848803,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-15T15:24:51.572Z","status":"ssl_error","status_checked_at":"2026-04-15T15:24:39.138Z","response_time":63,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["material-ui","mobx-react","react","react-hook-forms","universal-router"],"created_at":"2024-10-03T22:24:39.277Z","updated_at":"2026-04-15T16:08:29.534Z","avatar_url":"https://github.com/ndrean.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# A React app skeleton using Mobx, Universal Router, Material-UI, React-Hook-Forms\n\n## Deploy\n\n### Netlify\n\nJust go to \u003chttps://netlify.com\u003e ....\n\n### Surge\n\nInstall the **surge cli** (\u003chttps://surge.sh/\u003e), run `yarn build`, then `surge` in the \"./build\" folder: name it and view the app at: \u003chttp://rmm.surge.sh/\u003e\n\n### Github pages\n\nInstall `yarn add gh-pages --dev`, then add to \"package.json\":\n\n```js\n\"homepage\": \"https://ndrean.github.io/navbar\",\n...\n\"scripts\": {\n  \"predeploy\": \"yarn build\",\n  \"deploy\": \"gh-pages -d build\"\n}\n```\n\nand run `yarn deploy`.\n\n## TODOs\n\n0. ~~All Material-UI imports with rewired/custom-cra~~\n1. ~~Modal for sign-in~~\n2. ~~dynamic badges in navbar on #contacts \u0026 #pages-views~~\n3. ~~Mobile format of navbar \u0026 drawer~~\n4. ~~navigation with drawer~~\n5. ~~Navigation with Universal Router:~~\n   1. ~~protected route with redirection with Univ Router~~\n   2. ~~Mode \"admin\" set with `localStorage` with **lazy** `useState` and change in `useEffect`~~\n   3. ~~nested dynamic route `/contacts/:id`~~\n6. ~~Dynamic form: new + delete dynamic form user~~\n7. ~~Implement fake POST and fake return token~~\n8. ~~Suspense/lazy for code splitting~~\n9. ~~Facebook login, in modal \u0026 login-form~~\n10. ~~create Alert with the return token: FB-token \u0026 user created with fake-token~~\n\n11. Compare with React Router for passing props vs Universal Router - Mobx ??\n\n## CRA \u0026 Material-UI config (to use imports)\n\n1- CRA with updated ServiceWorker: `npx create-react-app my-app --template cra-template-pwa`\n\n2- Configure **babel** with `babel-plugin-import`, create a `.babelrc.js` file in the root directory of your project. Since we use CRA, we need `react-app-rewired` and `customize-cra` to be able to use `.babelrc.js`.\n\n## Notes\n\n### Difficulties code bugs to remove from Mobx\n\n1- \"Cannot update a component from inside the function body of a different component.\"\n\n2- \"Cannot update a component (`_c`) while rendering a different component (`_c`)\"\n\nSome recipies:\n\n-\u003e try put in `useEffect` for one,\n\n-\u003e perform action in navbar method calling the component rather than in the component,\n\n-\u003e use `action` on every event method.\n\nBut no solution found for Modal: `(isSubmitSuccessful) store.setModalClose()` \u003c=\u003e pb 2.\n\n## React-hook-forms \u0026 Material-UI\n\n- need to register the form fields `TexField` component with `inputRef={register()}`\n\n- need to control the form field `Checkbox` component with:\n\n- add inputs \"on the fly\". Take an array of data and map the indexed form. Note that the name should be something like: `name={`data[${i}].email`}`where `i` is the index of the mapping.\n\nFor the checkbox, needed below:\n\n```js\ncontrol={\n   \u003cController as={Checkbox} control={control}  defaultValue={false} name=\"remember\"/\u003e\n```\n\n### Insert image in CardMedia - Material-UI from a folder\n\nTo insert several cards, map an array `cards` as shown in the example. Then needed to:\n\n1. `import...` (Webpack),\n2. use `component=\"img\"`\n3. use `image` tag \u003c=\u003e `background-image = url(\"...\")`\n\n\u003e Code example\n\n```js\nimport imgReact from \"../img/React.png\";\nimport imgMobx from \"../img/Mobx.jpeg\";\n...\nconst cards = [\n  {\n    name: \"React\",\n    image: imgReact,\n    url: \"https://fr.reactjs.org/\",\n  },\n  {\n    name: \"Mobx\",\n    image: imgMobx,\n    url: \"https://mobx.js.org/README.html\",\n  },\n  [...]\n}\n{cards.map(({ name, image, url }) =\u003e (...\n\u003cCardMedia component=\"img\" image={image} title={`tech: ${name}`} loading=\"lazy\" /\u003e\n```\n\n### To implement Cloudinary REST API to save pictures without back-end\n\n72x72\n\n- Input:\n\n```js\nimport PhotoCameraOutlinedIcon from '@material-ui/icons/PhotoCameraOutlined';\n\u003cinput type=\"file\" accept=\"image/*\" onClick={()=\u003e setImageFile(e.target.files[0])}/\u003e`\n```\n\n- send to the cloud: direct call Cloudinary REST API\n\n```js\nconst formData = new FormData();\nformData.append(\"file\", fileCL);\nformData.append(\"upload_preset\", \"ml_default\");\nfetch(`https://api.cloudinary.com/v1_1/${props.cloudname}/upload/`,\n  {\n    method: \"POST\",\n    body: formData,\n  }\n)\n  // CL response will be a SECURE_LINK and a unique ID\n  .then((res) =\u003e res.json())\n  .then((resCL) =\u003e {\n    console.log(resCL.public_id);\n    console.log(resCL.secure_url);\n```\n\n2- save into the Cloud or host file sytem\n\n3- preview with `URL.createObjectURL()` or with `readAsDataURL`\n\n```js\n\u003cinput type=\"file\" accept=\"image/*\" id=\"inputFile\"/\u003e\n\u003cimg id=\"previewElt\"/\u003e\nconst previewImg = document.getElementById(\"previewElt\")\nconst fileElt = document.getElementById(\"inputFile\")\n```\n\n4- save an image into a file with a \"fake\" `\u003ca\u003e` where we simulate the `click()` and using `new Blob`:\n\n\u003chttps://robkendal.co.uk/blog/2020-04-17-saving-text-to-client-side-file-using-vanilla-js\u003e\n\n```js\nconst contentType = \"image/*\"; // or `text/plain\" .or \"text/html\" or...\nconst a = document.createElement(\"a\");\nconst blob = new Blob([content], { type: contentType });\na.href = URL.createObjectURL(blob);\na.download = \"filename\"; // the name given to the file: to be changed\na.click(); // simulate the click\nURL.revokeObjectURL(a.href);\n```\n\nOR with `new FileReader()`\n\n```js\nfileElt.addEventListener(\"change\", () =\u003e {\n  const file = e.target.files[0];\n  const url = URL.createObjectURL(file);\n  previewImg.scr = url;\n```\n\n4- Resize an image: `canvas` and `readAsDataURL` and `canvasToBlob`\n\n\u003chttps://htmldom.dev/resize-an-image/\u003e\n\n4- display\n\n## Code notes\n\n### To view on mobile phone the dev stage\n\n`http-server ./build -a 0.0.0.0 -p 8090` and open **192.168.0.41:8090**\n\n### Use formdata with RHF\n\n```js\nconst formRef = React.useRef(null)\n\nconst data = new FormData(formRef.current)\nfetch(\"url\", {\n  method: \"POST\",\n  mode: \"cors\",\n  body: data,\n  }\n})\n\u003cform ... ref={formRef}\u003e\n```\n\n\u003e !!!! NO CONTENT-TYPE ..... !!!!\n\n### Dynamic \"on-the-fly\" form creation with RHF\n\nIf we want to produce a payload like:\n\n```js\nusers: [{ email: \"\", password: \"\"},...,{ email: \"\", password: \"\"}]\n```\n\nthen we will use `const [users, setUSers]=React.useState(\"\")` and we use the naming:\n\n```js\n{users.map((_,i)=\u003e {\n  \u003cTextField key={i}\n  ...\n  name={users[${i}].email}\n  /\u003e\n  ...\n})}\n```\n\n### Note on Promise.all and useEffect with async\n\nWe will fetch several pages, so make several calls. Then we will run several `json()` calls. Use `Promise.all`.\n\n```js\nuseEffect(() =\u003e {\n  async function fetchData(){\n    const urlApi2 = \"https://reqres.in/api/users\";\n    const requests = [1, 2].map((page) =\u003e fetch(`${urlApi2}?page=${page}`));\n    const response = await Promise.all(requests);\n    const users = await Promise.all(response.map((r) =\u003e r.json()));\n    return users.flatMap((u) =\u003e u.data);\n  }\n  fetchData().then(....)\n}, []);\n```\n\n\u003chttps://javascript.info/promise-api\u003e\n\n### Note on arrays of objects\n\n\u003e replace an object in the array on attribute change\n\n```js\nconst A = [{\"a\":1, \"b\":true},{\"a\":2,\"b\":false}]\nconst [values, setValues] = useState(A)\n// we want to replace with newValue = {\"a\":2,\"b\":true}\nsetValues(prev =\u003e {\n  return prev.map(obj=\u003e {\n    if (obj.a===newValue.a) return {...obj, newValue.b};\n  return obj\n  })\n})\n```\n\n\u003e Append to an array of objects if not present\n\n```js\nconst store = [\n  { a: 1, b: true },\n  { a: 2, b: false },\n];\nconst [values, setValues] = useState(store);\nconst result = [\n  { a: 2, b: true },\n  { a: 3, b: false },\n];\nconst keysOfAs = Array.from(store, ({ a }) =\u003e a); // array of keys values\nsetValues((prev) =\u003e {\n  for (const obj of result) {\n    if (!keysOfAs.includes(obj.a)) [...prev, obj];\n  }\n});\n```\n\n### a Loader/Spinner\n\n```css\n.loader {\n  display: inline-block;\n  width: 40px;\n  height: 40px;\n}\n\n.loader:after {\n  content: \" \";\n  display: block;\n  width: 30px;\n  height: 30px;\n  margin: 1px;\n  border-radius: 50%;\n  border: 5px solid #fff;\n  border-color: #fff transparent #fff transparent;\n  animation: loader 1.2s linear infinite;\n}\n\n@keyframes loader {\n  0% {\n    transform: rotate(0deg);\n  }\n  100% {\n    transform: rotate(360deg);\n  }\n}\n```\n\n### Make a phone call\n\n`\u003ca href=\"tel:60305520\"\u003eCall me!\u003c/a\u003e`\n\n## Bundle analysis\n\n`yarn add source-map-explorer` and add `\"analyze\": \"source-map-explorer 'build/static/js/*.js'\"` in **package.json**.\n\n## Available Scripts\n\n`yarn start`\n`yarn build`\n`yarn analyze`\n`static-server` from the \"/build\" folder.\n\nWith the conflict of Ruby's `serve`, used **static-server** to run the build file from the folder \"./build\". \u003chttps://www.npmjs.com/package/static-server\u003e\n\n### `yarn eject`\n\n**Note: this is a one-way operation. Once you `eject`, you can’t go back!**\n\nIf 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.\n\nInstead, 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.\n\nYou 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.\n\n## Learn More\n\nYou can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).\n\nTo learn React, check out the [React documentation](https://reactjs.org/).\n\n### Code Splitting\n\nThis section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)\n\n### Analyzing the Bundle Size\n\nThis section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)\n\n### Making a Progressive Web App\n\nThis section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)\n\n### Advanced Configuration\n\nThis section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)\n\n### Deployment\n\nThis section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)\n\n### `yarn build` fails to minify\n\nThis section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)\n\n```\n\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fndrean%2Fnavbar","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fndrean%2Fnavbar","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fndrean%2Fnavbar/lists"}