React: ์ž˜๋ชป๋œ ํ›„ํฌ ํ˜ธ์ถœ์ž…๋‹ˆ๋‹ค. ํ›„ํฌ๋Š” ํ•จ์ˆ˜ ๊ตฌ์„ฑ ์š”์†Œ์˜ ๋ณธ๋ฌธ ๋‚ด๋ถ€์—์„œ๋งŒ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ์ด์œ  ์ค‘ ํ•˜๋‚˜๋กœ ์ธํ•ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์— ๋งŒ๋“  2019๋…„ 04์›” 04์ผ  ยท  61์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: facebook/react

์•ˆ๋…•ํ•˜์„ธ์š”, ์ €๋Š” ๋ฐ˜์‘ํ•˜๋Š” ๊ฒƒ์ด ์ฒ˜์Œ์ด๊ณ  ๊ตฌ์„ฑ ์š”์†Œ์˜ ๋ฐ˜์‘ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋งŒ๋“ค๋ ค๊ณ ํ•˜๋Š”๋ฐ ๋‚ด๊ฐ€ ๋งŒ๋“ค๊ณ ์žˆ๋Š” ๊ตฌ์„ฑ ์š”์†Œ ์ค‘ ํ•˜๋‚˜๊ฐ€ REACT HOOKS ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์—์ด ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ฉด์ฑ… ์กฐํ•ญ: ๋ฌธ์ œ๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์€ ์ด๋ฒˆ์ด ์ฒ˜์Œ์ด๋ฏ€๋กœ ์–‘ํ•ดํ•ด ์ฃผ์‹ญ์‹œ์˜ค.

๊ทธ๋ž˜์„œ accordion__item--open ๋ฐ accordion__item ํด๋ž˜์Šค ์‚ฌ์ด๋ฅผ ํ† ๊ธ€ํ•˜์—ฌ ์—ด๊ณ  ๋‹ซ๋Š” ์•„์ฝ”๋””์–ธ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๋งŒ๋“ค๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

ํŒจํ‚ค์ง€.json

{
  "name": "react-lib",
  "version": "0.3.0",
  "description": "A simple UI library of react components",
  "main": "dist/index.js",
  "publishConfig": {
    "registry": ""
  },
  "scripts": {
    "login": "",
    "build": "webpack --mode=production",
    "develop": "webpack --mode=development --watch"
  },
  "repository": {
    "type": "git",
    "url": ""
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "homepage": "",
  "dependencies": {
    "@babel/core": "^7.3.4",
    "@babel/preset-env": "^7.3.4",
    "@babel/preset-react": "^7.0.0",
    "babel-loader": "^8.0.5",
    "eslint": "^5.15.1",
    "eslint-loader": "^2.1.2",
    "webpack": "^4.29.6",
    "webpack-cli": "^3.2.3",
    "webpack-node-externals": "^1.7.2"
  },
  "devDependencies": {
    "eslint-config-airbnb": "^17.1.0",
    "eslint-plugin-import": "^2.16.0",
    "eslint-plugin-jsx-a11y": "^6.2.1",
    "eslint-plugin-react": "^7.12.4",
    "eslint-plugin-react-hooks": "^1.6.0"
  }
}

์›นํŒฉ.config.js

const path = require('path');
const webpack = require('webpack');

module.exports =  {

  mode: 'development',
  optimization: {
    minimize: true,
  },
  entry: './index.js',
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'index.js',
    library: '',
    libraryTarget: 'commonjs'
  },
  target: 'node',
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules)/,
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-env', '@babel/react']
        }
      }
    ]
  } 
};

์ด๊ฒƒ์€ ์•„์ฝ”๋””์–ธ ์ปจํ…Œ์ด๋„ˆ์ž…๋‹ˆ๋‹ค.

```
'๋ฐ˜์‘'์—์„œ React ๊ฐ€์ ธ์˜ค๊ธฐ;

๊ธฐ๋Šฅ ์•„์ฝ”๋””์–ธ({์ž์‹ }) {

๋ฐ˜ํ’ˆ (


{ ์–ด๋ฆฐ์ด๋“ค }

);

}

๊ธฐ๋ณธ ์•„์ฝ”๋””์–ธ ๋‚ด๋ณด๋‚ด๊ธฐ;

**This is the accordion item that will live inside the container:** 

'๋ฐ˜์‘'์—์„œ React, { useState } ๊ฐ€์ ธ์˜ค๊ธฐ;

๊ธฐ๋Šฅ AccordionItem({ํ—ค๋”, ์ฝ˜ํ…์ธ }) {

const [ isActive, toggleActive ] = useState(๊ฑฐ์ง“);

๋ฐ˜ํ’ˆ (

accordion__item ${ isActive ? 'accordion__item--open' : '' } }>
  <button
    className="accordion__item-header"
    onClick={ () => isActive ? toggleActive(false) : toggleActive(true) }
  >
   { header }
   <svg className="icon icon--debit" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50">
     <path className="icon__path" d="M22.37,22.33V2.67a2.63,2.63,0,0,1,5.26,0V47.24a2.63,2.63,0,0,1-5.26.09V27.58H2.71a2.63,2.63,0,0,1,0-5.25Zm11.92,5.25a2.63,2.63,0,0,1,0-5.25h13a2.63,2.63,0,0,1,0,5.25Z">
     </path>
   </svg>
 </button>

 <div className="accordion__item-content">
   { children }
 </div>


)

};

๊ธฐ๋ณธ AccordionItem ๋‚ด๋ณด๋‚ด๊ธฐ;

Now inside of a [create-react-app](https://github.com/sethandleah/myapp) I import these components

My [library](https://github.com/sethandleah/react-lib) and the [create-react-app](https://github.com/sethandleah/myapp) are relative to each other and I am using ```npm link```

'๋ฐ˜์‘'์—์„œ React ๊ฐ€์ ธ์˜ค๊ธฐ;

'react-lib'์—์„œ {AccordionItem} ๊ฐ€์ ธ์˜ค๊ธฐ
'react-lib'์—์„œ {Accordion} ๊ฐ€์ ธ์˜ค๊ธฐ;

ํ•จ์ˆ˜ ์•ฑ({props}) {

๋ฐ˜ํ’ˆ (

  <Accordion>
    <AccordionItem header={"Hello"} content={"World"}/>
  </Accordion>

)

}

๊ธฐ๋ณธ ์•ฑ ๋‚ด๋ณด๋‚ด๊ธฐ;

I have followed all of these [instructions](https://reactjs.org/warnings/invalid-hook-call-warning.html) and I still get the same error.


**Current behavior?**

์ž˜๋ชป๋œ ํ›„ํฌ ํ˜ธ์ถœ์ž…๋‹ˆ๋‹ค. ํ›„ํฌ๋Š” ํ•จ์ˆ˜ ๊ตฌ์„ฑ ์š”์†Œ์˜ ๋ณธ๋ฌธ ๋‚ด๋ถ€์—์„œ๋งŒ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ์ด์œ  ์ค‘ ํ•˜๋‚˜๋กœ ์ธํ•ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  1. React์™€ ๋ Œ๋”๋Ÿฌ(์˜ˆ: React DOM)์˜ ๋ฒ„์ „์ด ์ผ์น˜ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. Hooks์˜ ๊ทœ์น™์„ ์œ„๋ฐ˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  3. ๋™์ผํ•œ ์•ฑ์— ๋‘ ๊ฐœ ์ด์ƒ์˜ React ์‚ฌ๋ณธ์ด ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    ์ด ๋ฌธ์ œ๋ฅผ ๋””๋ฒ„๊ทธํ•˜๊ณ  ์ˆ˜์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ํŒ์€ https://fb.me/react-invalid-hook-call ์„ ์ฐธ์กฐ
**Steps to reproduce**

- clone [https://github.com/sethandleah/react-lib](https://github.com/sethandleah/react-lib)
- clone [https://github.com/sethandleah/myapp](https://github.com/sethandleah/myapp)
- ```cd react-lib```
- ```npm install```
- ```npm link```
- ```cd ../myapp```
- ```npm i```
- ```npm link react-lib```
- ```npm start```

**Expected behavior**

- It should show a button with a "plus" svg sign and the words "Hello" and "World" respectively
- Open devtools and go to elements
- When clicking on the button the class ```accordion_item--open``` should toggle

**To see the above, do the following:**

- Uncomment these lines at ```myapp/src/App.js```

'./Accordion'์—์„œ ์•„์ฝ”๋””์–ธ ๊ฐ€์ ธ์˜ค๊ธฐ;
'./AccordionItem'์—์„œ AccordionItem ๊ฐ€์ ธ์˜ค๊ธฐ;

- The comment out these line, alse at ```myapp/src/App.js```:

'react-lib'์—์„œ { ์•„์ฝ”๋””์–ธ } ๊ฐ€์ ธ์˜ค๊ธฐ;
'react-lib'์—์„œ { AccordionItem } ๊ฐ€์ ธ์˜ค๊ธฐ;
```

React, Browser/OS ๋ฒ„์ „์€ ์ด ๋ฌธ์ œ์˜ ์˜ํ–ฅ์„ ๋ฐ›์Šต๋‹ˆ๋‹ค.

  • React ๋ฐ React-Dom: ๋‘˜ ๋‹ค react-lib ๋ฐ myapp ๋ชจ๋‘์—์„œ ^16.8.6 ์ž…๋‹ˆ๋‹ค.
  • ๋ธŒ๋ผ์šฐ์ €: Brave Version 0.61.52 Chromium: 73.0.3683.86 (Official Build) (64-bit)
  • OS: MacOS ํ•˜์ด ์‹œ์—๋ผ Version 10.13.6 (17G5019)
Needs Investigation

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

๋‚ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ create-react-app์€ ์„œ๋กœ ์ƒ๋Œ€์ ์ด๋ฉฐ npm ๋งํฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ถ€๋ถ„์„ ์ฝ์—ˆ์Šต๋‹ˆ๊นŒ?

https://reactjs.org/warnings/invalid-hook-call-warning.html#duplicate - ๋ฐ˜์‘

link ์›Œํฌํ”Œ๋กœ๋ฅผ ์ง์ ‘ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

Screen Shot 2019-04-04 at 09 28 47

๋ชจ๋“  61 ๋Œ“๊ธ€

https://github.com/facebook/react/issues/13991 ์˜ ์‚ฌ๋žŒ๋“ค๊ณผ ๋™์ผํ•œ ๋ฌธ์ œ์— ์ง๋ฉดํ•˜๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋ฌธ์ œ์˜ ๋งจ ์•„๋ž˜์— ์ œ๊ณต๋œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• ์ค‘ ์ผ๋ถ€๋ฅผ ์‚ดํŽด๋ณด๊ณ  ๋ฌธ์ œ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹น์‹ ์„ ์œ„ํ•ด ์ผ?

์ด๊ฒƒ์€ HOC ๋‚ด๋ถ€์—์„œ ํ›„ํฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

const HOC = Component => {
  return (props) => {
    const [val] = useState();
    return <div>{val}</div>
  }
}

@revskill10 ์ฝ”๋“œ์ƒŒ๋“œ ๋ฐ•์Šค ์žฌํ˜„์œผ๋กœ ๋ณ„๋„์˜ ์ด์Šˆ๋ฅผ ๋งŒ๋“ค์–ด์ฃผ์„ธ์š”.

https://www.npmjs.com/package/webpack-bundle-analyzer ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค

๋‘ ๊ฐœ์˜ ๋ฐ˜์‘ ๋นŒ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์ด ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ค๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๊ฒฝ์šฐ ๋ฐ˜์‘ ๋ฐ ๋ฐ˜์‘ ์˜์—ญ์„ ์ƒ์œ„ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์— ์—ฐ๊ฒฐํ•˜๋Š” ๊ฒƒ์„ ์ข…์ข… ์žŠ์—ˆ์Šต๋‹ˆ๋‹ค.

์ด ๊ฒฝ์šฐ npm์€ react ๋ฐ react-dom์„ react ๋ฐ react-dom์˜ ์ƒ์œ„ ๋ฒ„์ „์— ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

๋‚ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ create-react-app์€ ์„œ๋กœ ์ƒ๋Œ€์ ์ด๋ฉฐ npm ๋งํฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ถ€๋ถ„์„ ์ฝ์—ˆ์Šต๋‹ˆ๊นŒ?

https://reactjs.org/warnings/invalid-hook-call-warning.html#duplicate - ๋ฐ˜์‘

link ์›Œํฌํ”Œ๋กœ๋ฅผ ์ง์ ‘ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

Screen Shot 2019-04-04 at 09 28 47

(์ด ๋ฐฉ๋ฒ•์ด ๋„์›€์ด ๋˜์ง€ ์•Š์œผ๋ฉด ์•Œ๋ ค์ฃผ์‹œ๋ฉด ๋‹ค์‹œ ์—ด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž์„ธํžˆ ์ ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.)

์ˆ˜๊ณ ํ•ด์ฃผ์‹  ๋ชจ๋“  ๋ถ„๋“ค ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค

@gaearon ๋‚˜๋Š” ์ด๋ฏธ React ํŽ˜์ด์ง€์˜ ๊ถŒ์žฅ ์‚ฌํ•ญ์— ๋”ฐ๋ผ ์ด๊ฒƒ์„ ํ•ด๊ฒฐํ•˜๋ ค๊ณ  ์‹œ๋„ํ•˜๊ณ  ์žฌ์‹œ๋„ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ชจ๋“  ์ง€์นจ ์„ ๋”ฐ๋ž์ง€๋งŒ ์—ฌ์ „ํžˆ ๊ฐ™์€ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

@threepointone ์ถฉ๋ถ„ํžˆ ์›ƒ๊ธฐ๊ณ  ๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด #13991 ์—์„œ ์ด์— ๋Œ€ํ•œ ํ•ด๊ฒฐ์ฑ…์„

์•Œ๊ฒ ์Šต๋‹ˆ๋‹ค. ๋„์›€์ด ๋˜์ง€ ์•Š์œผ๋ฉด ๋ˆ„๊ตฐ๊ฐ€ ๋ฌธ์ œ๋ฅผ ๋””๋ฒ„๊น…ํ•˜๊ณ  ๋„์›€์„ ์ค„ ์ˆ˜ ์žˆ๋„๋ก ์—ด์–ด ๋‘ก๋‹ˆ๋‹ค. ๋ฌธ์ œ๋Š” _is_ ๋™์ผํ•˜์ง€๋งŒ(ํŒจํ‚ค์ง€๋ฅผ ๋งํฌํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ธํ•ด ํŒจํ‚ค์ง€๊ฐ€ ๋ฒˆ๋“ค์— ๋ณต์ œ๋จ) ์ œ์•ˆ์ด ํšจ๊ณผ๊ฐ€ ์—†๋Š” ์ด์œ ๊ฐ€ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

@gaearon ์ด์ œ ๋‹ซ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์œ„์˜ ์†”๋ฃจ์…˜์ด ์ž‘๋™ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ €๋Š” ์•„์ฃผ ๊ธฐ๋ณธ์ ์ธ ๋ฐฉ์‹์œผ๋กœ hook์„ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

import React, {useState} from 'react'
import ReactDOM from 'react-dom'

function App() {
const [number, setNumber] = useState(0);
const increase = () => {
    setNumber(number+1);
}
return <div>
<span>Counting: {number}</span>
<button onClick={increase} >Increase</button>
</div>
}

const selector = document.getElementById('app');

ReactDOM.render(<App />, selector);

๋‚˜๋Š” ์˜ค๋ฅ˜๋ฅผ ์–ป๋Š”๋‹ค
Hooks can only be called inside of the body of a function component.
์Šค์ผ€์น˜์—์„œ ๋ฐ˜์‘ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŽ์ด ์ƒ์„ฑํ–ˆ์ง€๋งŒ ์ด์ „์—๋Š” ์ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.
ํ›„ํฌ์˜ ๋ชจ๋“  ๊ทœ์น™์„ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค.
๋””๋ฒ„๊น…ํ•˜๊ณ  ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด 12์‹œ๊ฐ„ ๋™์•ˆ ์‹œ๊ฐ„์„ โ€‹โ€‹ํ• ์• ํ•˜์ง€๋งŒ ๋ถˆํ–‰ํžˆ๋„ ๊ทธ๋Ÿด ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ ์ž˜ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค.

// Add this in node_modules/react-dom/index.js
window.React1 = require('react');

// Add this in your component file
require('react-dom');
window.React2 = require('react');
console.log(window.React1 === window.React2); // I get true

@xeastcrast ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๊นŒ?

@carlosriveros
์‚ฌ๋ก€ 1:
ํ•˜๋‚˜์˜ ์Šคํฌ๋ฆฝํŠธ ํƒœ๊ทธ ๋ฒˆ๋“ค๋งŒ ์žˆ๋Š” index.html์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

index.html์„ ํ™•์ธํ•œ ํ›„์—๋„ ๋™์ผํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ์šฐ
์‚ฌ๋ก€ 2:
Webpack html์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ webpack.config ํŒŒ์ผ ๋‚ด์˜ ํ”Œ๋Ÿฌ๊ทธ์ธ ์†์„ฑ
๊ทธ๋ฆฌ๊ณ  new HTMLWebpackPlugin()์˜ ์˜ต์…˜์— {reject: true}๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

index.html์—์„œ ๋ฒˆ๋“ค๋กœ srcํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ ํƒœ๊ทธ๋ฅผ ์ œ๊ฑฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

HTMLWebpackPlugin์€ ๋ฒˆ๋“ค ํŒŒ์ผ์„ ์ž๋™์œผ๋กœ ํฌํ•จํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ ํƒœ๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์•ˆ๋…•ํ•˜์„ธ์š”, ์ €๋Š” React Hooks๋ฅผ ์ฒ˜์Œ ์ ‘ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ฐฉ๊ธˆ ๋‚ด ํ”„๋กœ์ ํŠธ์—์„œ ๊ฒ€์ƒ‰ ํ‘œ์‹œ์ค„์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด Material-UI๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ–ˆ์ง€๋งŒ ์ž‘๋™ํ•˜์ง€ ์•Š๊ณ  ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” React Hooks ๋ฌธ์„œ๋ฅผ ์ฝ์—ˆ์ง€๋งŒ ์ด๊ฒƒ์— ๊ด€ํ•ด ์•„๋ฌด๊ฒƒ๋„ ์–ป์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.
REACTJS ์ฝ”๋“œ
```'react'์—์„œ React ๊ฐ€์ ธ์˜ค๊ธฐ;
'@material-ui/core/styles'์—์„œ { makeStyles } ๊ฐ€์ ธ์˜ค๊ธฐ;
'@material-ui/core/Paper'์—์„œ ์ข…์ด ๊ฐ€์ ธ์˜ค๊ธฐ;
'@material-ui/core/InputBase'์—์„œ InputBase ๊ฐ€์ ธ์˜ค๊ธฐ;
'@material-ui/core/IconButton'์—์„œ IconButton ๊ฐ€์ ธ์˜ค๊ธฐ;
'@material-ui/icons/Search'์—์„œ SearchIcon ๊ฐ€์ ธ์˜ค๊ธฐ;

const useStyles = makeStyles({
๋ฃจํŠธ: {
ํŒจ๋”ฉ: '2px 4px',
๋””์Šคํ”Œ๋ ˆ์ด: 'ํ”Œ๋ ‰์Šค',
alignItems: '์ค‘์•™',
๋„ˆ๋น„: 400,
},
์ž…๋ ฅ: {
์—ฌ๋ฐฑ์™ผ์ชฝ: 8,
ํ”Œ๋ ‰์Šค: 1,
},
์•„์ด์ฝ˜ ๋ฒ„ํŠผ: {
ํŒจ๋”ฉ: 10,
},
๊ตฌ๋ถ„์„ : {
๋„ˆ๋น„: 1,
ํ‚ค: 28,
์—ฌ๋ฐฑ: 4,
},
});

๊ธฐ๋Šฅ ๊ธฐ๋Šฅ() {
const ํด๋ž˜์Šค = useStyles();

๋ฐ˜ํ’ˆ (





);
}

๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๋‚ด๋ณด๋‚ด๊ธฐ;

**PACKAGE.JSON**
```{
  "name": "builder-frontend",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@material-ui/core": "^4.0.1",
    "@material-ui/docs": "^3.0.0-alpha.9",
    "@material-ui/icons": "^4.0.0",
    "@material-ui/styles": "^4.0.1",
    "react": "^16.8.5",
    "react-dom": "^16.8.5",
    "react-redux": "^6.0.1",
    "react-router-dom": "^5.0.0",
    "react-scripts": "2.1.8",
    "redux": "^4.0.1",
    "styled-components": "^4.2.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": [
    ">0.2%",
    "not dead",
    "not ie <= 11",
    "not op_mini all"
  ]
}

๋ฒ„์ „

  • ๋…ธ๋“œ -- v10.15.3
  • ๋ฐ˜์‘ -- ^16.8.5
  • ์žฌ๋ฃŒ-UI -- ^4.0.1

์˜ˆ์ƒ ๊ฒฐ๊ณผ

  • ๋‚ด์žฅ VS CODE ๋ฐ ์˜ˆ์ƒ _๊ฒ€์ƒ‰์ฐฝ_
    ์‚ฐ์ถœ
    _TypeError: Object(...)๋Š” ํ•จ์ˆ˜๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค_
    _์ž˜๋ชป๋œ ํ›„ํฌ ํ˜ธ์ถœ์ž…๋‹ˆ๋‹ค. ํ›„ํฌ๋Š” ํ•จ์ˆ˜ ๊ตฌ์„ฑ ์š”์†Œ์˜ ๋ณธ๋ฌธ ๋‚ด๋ถ€์—์„œ๋งŒ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ์ด์œ  ์ค‘ ํ•˜๋‚˜๋กœ ์ธํ•ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค._

๋…ธ๋ ฅํ•˜๋‹ค
_CODESANDBOX์—์„œ ์‹คํ–‰ํ•ด ๋ณด์‹ญ์‹œ์˜ค . ๋ชจ๋“  ๊ฒƒ์ด ์ž˜ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค_ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค .

์•ˆ๋…•ํ•˜์„ธ์š”, ์ €๋Š” React Hooks๋ฅผ ์ฒ˜์Œ ์ ‘ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ฐฉ๊ธˆ ๋‚ด ํ”„๋กœ์ ํŠธ์—์„œ ๊ฒ€์ƒ‰ ํ‘œ์‹œ์ค„์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด Material-UI๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ–ˆ์ง€๋งŒ ์ž‘๋™ํ•˜์ง€ ์•Š๊ณ  ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” React Hooks ๋ฌธ์„œ๋ฅผ ์ฝ์—ˆ์ง€๋งŒ ์ด๊ฒƒ์— ๊ด€ํ•ด ์•„๋ฌด๊ฒƒ๋„ ์–ป์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.
REACTJS ์ฝ”๋“œ

import { makeStyles } from '@material-ui/core/styles';
import Paper from '@material-ui/core/Paper';
import InputBase from '@material-ui/core/InputBase';
import IconButton from '@material-ui/core/IconButton';
import SearchIcon from '@material-ui/icons/Search';

const useStyles = makeStyles({
  root: {
    padding: '2px 4px',
    display: 'flex',
    alignItems: 'center',
    width: 400,
  },
  input: {
    marginLeft: 8,
    flex: 1,
  },
  iconButton: {
    padding: 10,
  },
  divider: {
    width: 1,
    height: 28,
    margin: 4,
  },
});

function Feature() {
    const classes = useStyles();

  return (
    <Paper className={classes.root}>
      <InputBase className={classes.input} placeholder="Search Google Maps" />
      <IconButton className={classes.iconButton} aria-label="Search">
        <SearchIcon />
      </IconButton>
    </Paper>
  );
}

export default Feature;

ํŒจํ‚ค์ง€.JSON

  "name": "builder-frontend",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@material-ui/core": "^4.0.1",
    "@material-ui/docs": "^3.0.0-alpha.9",
    "@material-ui/icons": "^4.0.0",
    "@material-ui/styles": "^4.0.1",
    "react": "^16.8.5",
    "react-dom": "^16.8.5",
    "react-redux": "^6.0.1",
    "react-router-dom": "^5.0.0",
    "react-scripts": "2.1.8",
    "redux": "^4.0.1",
    "styled-components": "^4.2.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": [
    ">0.2%",
    "not dead",
    "not ie <= 11",
    "not op_mini all"
  ]
}

๋ฒ„์ „

  • ๋…ธ๋“œ -- v10.15.3
  • ๋ฐ˜์‘ -- ^16.8.5
  • ์žฌ๋ฃŒ-UI -- ^4.0.1

์˜ˆ์ƒ ๊ฒฐ๊ณผ

  • ๋‚ด์žฅ VS CODE ๋ฐ ์˜ˆ์ƒ _๊ฒ€์ƒ‰์ฐฝ_
    ์‚ฐ์ถœ
    _TypeError: Object(...)๋Š” ํ•จ์ˆ˜๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค_
    _์ž˜๋ชป๋œ ํ›„ํฌ ํ˜ธ์ถœ์ž…๋‹ˆ๋‹ค. ํ›„ํฌ๋Š” ํ•จ์ˆ˜ ๊ตฌ์„ฑ ์š”์†Œ์˜ ๋ณธ๋ฌธ ๋‚ด๋ถ€์—์„œ๋งŒ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ์ด์œ  ์ค‘ ํ•˜๋‚˜๋กœ ์ธํ•ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค._

๋…ธ๋ ฅํ•˜๋‹ค
_CODESANDBOX์—์„œ ์‹คํ–‰ํ•ด ๋ณด์‹ญ์‹œ์˜ค . ๋ชจ๋“  ๊ฒƒ์ด ์ž˜ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค_ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค .

๋‚˜๋Š” ๋˜ํ•œ material-ui๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ด ์งˆ๋ฌธ์„ ๋งŒ๋‚œ๋‹ค.

@stupidsongshu , ๊ทธ๋ž˜์„œ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•˜์…จ๋‚˜์š”?

์•ˆ๋…•ํ•˜์„ธ์š”, ์ €๋Š” React Hooks๋ฅผ ์ฒ˜์Œ ์ ‘ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ฐฉ๊ธˆ ๋‚ด ํ”„๋กœ์ ํŠธ์—์„œ ๊ฒ€์ƒ‰ ํ‘œ์‹œ์ค„์„ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด Material-UI๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ–ˆ์ง€๋งŒ ์ž‘๋™ํ•˜์ง€ ์•Š๊ณ  ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” React Hooks ๋ฌธ์„œ๋ฅผ ์ฝ์—ˆ์ง€๋งŒ ์ด๊ฒƒ์— ๊ด€ํ•ด ์•„๋ฌด๊ฒƒ๋„ ์–ป์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.
REACTJS ์ฝ”๋“œ

import { makeStyles } from '@material-ui/core/styles';
import Paper from '@material-ui/core/Paper';
import InputBase from '@material-ui/core/InputBase';
import IconButton from '@material-ui/core/IconButton';
import SearchIcon from '@material-ui/icons/Search';

const useStyles = makeStyles({
  root: {
    padding: '2px 4px',
    display: 'flex',
    alignItems: 'center',
    width: 400,
  },
  input: {
    marginLeft: 8,
    flex: 1,
  },
  iconButton: {
    padding: 10,
  },
  divider: {
    width: 1,
    height: 28,
    margin: 4,
  },
});

function Feature() {
    const classes = useStyles();

  return (
    <Paper className={classes.root}>
      <InputBase className={classes.input} placeholder="Search Google Maps" />
      <IconButton className={classes.iconButton} aria-label="Search">
        <SearchIcon />
      </IconButton>
    </Paper>
  );
}

export default Feature;

ํŒจํ‚ค์ง€.JSON

  "name": "builder-frontend",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@material-ui/core": "^4.0.1",
    "@material-ui/docs": "^3.0.0-alpha.9",
    "@material-ui/icons": "^4.0.0",
    "@material-ui/styles": "^4.0.1",
    "react": "^16.8.5",
    "react-dom": "^16.8.5",
    "react-redux": "^6.0.1",
    "react-router-dom": "^5.0.0",
    "react-scripts": "2.1.8",
    "redux": "^4.0.1",
    "styled-components": "^4.2.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": [
    ">0.2%",
    "not dead",
    "not ie <= 11",
    "not op_mini all"
  ]
}

๋ฒ„์ „

  • ๋…ธ๋“œ -- v10.15.3
  • ๋ฐ˜์‘ -- ^16.8.5
  • ์žฌ๋ฃŒ-UI -- ^4.0.1

์˜ˆ์ƒ ๊ฒฐ๊ณผ

  • ๋‚ด์žฅ VS CODE ๋ฐ ์˜ˆ์ƒ _๊ฒ€์ƒ‰์ฐฝ_
    ์‚ฐ์ถœ
    _TypeError: Object(...)๋Š” ํ•จ์ˆ˜๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค_
    _์ž˜๋ชป๋œ ํ›„ํฌ ํ˜ธ์ถœ์ž…๋‹ˆ๋‹ค. ํ›„ํฌ๋Š” ํ•จ์ˆ˜ ๊ตฌ์„ฑ ์š”์†Œ์˜ ๋ณธ๋ฌธ ๋‚ด๋ถ€์—์„œ๋งŒ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ์ด์œ  ์ค‘ ํ•˜๋‚˜๋กœ ์ธํ•ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค._

๋…ธ๋ ฅํ•˜๋‹ค
_CODESANDBOX์—์„œ ์‹คํ–‰ํ•ด ๋ณด์‹ญ์‹œ์˜ค . ๋ชจ๋“  ๊ฒƒ์ด ์ž˜ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค_ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค .

์ด๊ฒƒ์€ ์˜ค๋Š˜ ๋‚˜์—๊ฒŒ ์ผ์–ด๋‚ฌ๊ณ  npm install --save react-dom@latest๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

https://reactjs.org/warnings/invalid-hook-call-warning.html#duplicate - ๋ฐ˜์‘

๋„์›€๋ง ํŽ˜์ด์ง€์— ์ œ๊ณต๋œ ์ง€์นจ์„ ๋”ฐ๋ž์ง€๋งŒ ์—ฌ์ „ํžˆ ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

๋‚˜๋ฅผ ์œ„ํ•ด, ์šฐ๋ฆฌ๋Š” ๋‹จ์ˆœํžˆ React๋ฅผ ์†Œํ’ˆ์œผ๋กœ ์ „๋‹ฌํ•˜์—ฌ ๊ทธ๊ฒƒ์„ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์šฐ๋ฆฌ๊ฐ€ ์™„์ „ํžˆ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋Š” ์•ฑ์ด์ž ๋ณ„๋„์˜ ๋ชจ๋“ˆ์ด๋ฏ€๋กœ ๋ชจ๋“  ์‚ฌ๋žŒ์—๊ฒŒ ์ ์šฉ๋˜๋Š”์ง€ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค.

์ €์—๊ฒŒ๋„ ๋„์›€์ด ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค..
React ๋ฐ React-DOM = 16.8.0 ๋ฐ Material-UI = 4.1.3 ์‚ฌ์šฉ

๋‚˜๋Š” ์ด ํ›„ํฌ ๋ฌธ์ œ์— ๋Œ€ํ•œ ๋ชจ๋“  ์†”๋ฃจ์…˜์„ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ํ›„ํฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๊ฐ€์ ธ์˜ค๋ฉด 2๊ฐœ์˜ ๋ฐ˜์‘ ์ธ์Šคํ„ด์Šค(lib์—์„œ ํ•˜๋‚˜, ๋‚ด ์•ฑ์—์„œ ํ•˜๋‚˜)๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” ๋ชจ๋“  ์ œ์•ˆ์„ ๋”ฐ๋ž์ง€๋งŒ ์ง€๊ธˆ๊นŒ์ง€ ์•„๋ฌด ๊ฒƒ๋„ ์ž‘๋™ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๊ฒƒ์ด ๋” ๋ช…ํ™•ํ•ด์งˆ ๋•Œ๊นŒ์ง€ ์ƒํƒœ๋กœ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๋งŒ๋“ค ๊ฒƒ์ž…๋‹ˆ๋‹ค ...

์—ฌ๊ธฐ์— ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‘ ๊ฐœ์˜ ๋ฐ˜์‘ ์ธ์Šคํ„ด์Šค๊ฐ€ ์žˆ๋Š” ํ”„๋กœ์ ํŠธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜๋‚˜๋Š” lib ์ด๊ณ  ํ•˜๋‚˜๋Š” app ๋ฅผ ์†Œ๋น„ํ•˜๋Š” lib ์ž…๋‹ˆ๋‹ค.

yarn.lock , package-lock.json , node_modules , dist , ์บ์‹œ ๋ฐ ๋นŒ๋“œ ํŒŒ์ผ์˜ ์ข…๋ฅ˜๋ฅผ ์ œ๊ฑฐํ•œ ๋‹ค์Œ yarn ๋˜๋Š” npm install ๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค npm install ์ž…๋‹ˆ๋‹ค. ์ด์ œ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค!

๋‚˜๋Š” ์ด๊ฒƒ์„ ๋ณ€ํ™”๋กœ ํ•ด๊ฒฐํ•œ๋‹ค

                <Route key={index} render={route.component} path={route.path} />

์—๊ฒŒ

                <Route key={index} component={route.component} path={route.path} />

ํ•˜์ง€๋งŒ ๋‚˜๋Š” ์™œ ๊ทธ๋Ÿฐ์ง€ ๋ชจ๋ฅด๊ฒ ๋‹ค :(

์‹ค์ˆ˜๋กœ ๋ Œ๋”๋งํ•˜๋Š” ๋Œ€์‹  ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ณต์‹ ๊ฐ€์ด๋“œ๋Š” ์ด ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์–ธ๊ธ‰ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.
๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค, xyj404
<Route path="/login" exact component={LoginForm} />

๋‚˜๋Š” ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ์ง€๋งŒ ์›์ธ์ด ๋‹ฌ๋ž์Šต๋‹ˆ๋‹ค. ์ž๋™ ๊ฐ€์ ธ์˜ค๊ธฐ ํ”Œ๋Ÿฌ๊ทธ์ธ ๋˜๋Š” ์Šค๋‹ˆํŽซ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋“  ์‚ฌ์šฉ์ž์˜ ๊ฒฝ์šฐ ๋™์ผํ•œ ๋Œ€์†Œ๋ฌธ์ž ๋ฐ˜์‘์—์„œ ๊ฐ€์ ธ์™€์•ผ ํ•ฉ๋‹ˆ๋‹ค.

import {useEffect} from 'react' ์™€ import {useEffect} from 'React' ๊ฐ€ ํ˜ผํ•ฉ๋˜์–ด ์žˆ์œผ๋ฉด ์ด ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” ๋˜ํ•œ์ด ๋ฌธ์ œ๊ฐ€ ์žˆ์ง€๋งŒ ์Šคํ† ๋ฆฌ ๋ถ์—์„œ ๋ชจ๋“  ๊ฒƒ์ด ์ž˜ ์ž‘๋™ํ•˜์ง€๋งŒ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์—์„œ ์‚ฌ์šฉํ•  ๋•Œ ๋™์ผํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

๊ฐ™์€ ๋ฌธ์ œ๋„ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค
์Šคํ† ๋ฆฌ๋ถ์—์„œ๋Š” ๊ดœ์ฐฎ์ง€๋งŒ ์•ฑ์„ ์‚ฌ์šฉํ•˜๋ฉด ์ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

npm test ์‹คํ–‰ํ•ด๋„ ๋™์ผํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์ง€๋งŒ ์•ฑ์€ ์ œ๋Œ€๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.
์—ฌ๊ธฐ ๋‚ด package.json , ์ œ์•ˆ ์‚ฌํ•ญ์ด ์žˆ์Šต๋‹ˆ๊นŒ? ๋‚˜๋Š” ์ •๋ง๋กœ ๋ถ™์–ด ์žˆ๊ณ ์ด ์˜ค๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ ์‹œ๊ฐ„์„ ๋‚ญ๋น„ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

{
  "name": "myproject",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@date-io/moment": "1.0.1",
    "@material-ui/core": "^3.9.3",
    "@material-ui/icons": "^3.0.1",
    "animate.css": "^3.7.0",
    "aws-amplify": "^0.4.8",
    "aws-amplify-react": "^0.1.54",
    "axios": "^0.18.0",
    "file-saver": "^2.0.0",
    "ics-js": "^0.10.2",
    "material-ui-pickers": "2.1.1",
    "moment": "^2.23.0",
    "normalize.css": "^8.0.1",
    "react": "^16.8.6",
    "react-app-polyfill": "^1.0.0",
    "react-date-range": "^1.0.0-beta",
    "react-dom": "^16.8.6",
    "react-google-maps": "^9.4.5",
    "react-jss": "^8.6.1",
    "react-router": "^4.3.1",
    "react-router-dom": "^4.3.1",
    "react-scripts": "3.0.1",
    "recharts": "^1.3.5",
    "redux": "^4.0.1",
    "styled-components": "^4.2.0",
    "sweetalert2": "^7.33.1",
    "sweetalert2-react-content": "^1.0.1"
  },
  "scripts": {
    "start:dev": "node  -r dotenv/config ./node_modules/react-scripts/bin/react-scripts start dotenv_config_path=./env/development.env",
    "start:stage": "node -r dotenv/config ./node_modules/react-scripts/bin/react-scripts start dotenv_config_path=./env/stage.env",
    "start:prod": "node -r dotenv/config ./node_modules/react-scripts/bin/react-scripts start dotenv_config_path=./env/production.env",
    "build:stage": "node --max_old_space_size=2048 --max_old_space_size=1024 -r dotenv/config ./node_modules/.bin/react-scripts build dotenv_config_path=./env/stage.env",
    "build:dev": "node --max_old_space_size=2048 -r dotenv/config ./node_modules/react-scripts/bin/react-scripts build dotenv_config_path=./env/development.env",
    "build:prod": "node --max_old_space_size=2048 -r dotenv/config ./node_modules/react-scripts/bin/react-scripts build dotenv_config_path=./env/production.env",
    "test": "node -r dotenv/config ./node_modules/.bin/react-scripts test dotenv_config_path=./env/development.env --runInBand",
    "eject": "react-scripts eject"
  },
  "devDependencies": {
    "@testing-library/jest-dom": "^4.0.0",
    "@testing-library/react": "^8.0.5",
    "@testing-library/react-hooks": "^1.1.0",
    "dotenv": "^6.0.0",
    "prettier": "^1.15.3",
    "react-test-renderer": "^16.8.6"
  },
  "browserslist": [
    ">0.2%",
    "not dead",
    "not ie <= 11",
    "not op_mini all"
  ]
}

npm ๋งํฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์—ฐ๊ฒฐ๋œ ๋ณ„๋„์˜ ์•ฑ์—์„œ makeStyles from @material-ui/core makeStyles ํ›„ํฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ react ๋ฐ react-dom ๋‘˜ ๋‹ค v16.8.6์„ ์‚ฌ์šฉํ•˜์—ฌ ์ด ์ƒํ™ฉ์— ์ง๋ฉดํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ฉ”์ธ ์•ฑ์„ react ๋ฐ react-dom v16.9.0์œผ๋กœ ์—…๊ทธ๋ ˆ์ด๋“œํ•˜๊ณ  ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ค๋Š” ํ›„ํฌ๋ฅผ ์‚ฌ์šฉํ•˜๋˜ ์•ฑ์„ ๋‹ค์‹œ ์—ฐ๊ฒฐํ•˜๋ฉด ๋ชจ๋“  ๊ฒƒ์ด ๋‹ค์‹œ ์ž‘๋™ํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ, @material-ui๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋„ ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ํŒŒ์•…ํ•˜๋Š” ๋ฐ ๋” ๋งŽ์€ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฝ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ Google์„ ๊ฒ€์ƒ‰ํ•˜์—ฌ ์ด์— ๋Œ€ํ•œ ํ•ด๊ฒฐ์ฑ…์„ ์ฐพ์•˜์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์— ์žˆ์Šต๋‹ˆ๋‹ค http://putridparrot.com/blog/react-material-ui-invalid-hook-call-hooks-can-only-be-call-inside-of-the-body-of-a-function-component/

์›๋ณธ ๊ฒŒ์‹œ๋ฌผ:

React ๋‚ด์—์„œ Material UI ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  TypeScript(์ •ํ™•ํ•˜๊ฒŒ๋Š” .tsx ํŒŒ์ผ ๋‚ด)๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ช‡ ๊ฐ€์ง€ ์˜ˆ๋ฅผ ์‚ดํŽด๋ณด๋ฉด ๋Ÿฐํƒ€์ž„์— "์ž˜๋ชป๋œ ํ›„ํฌ ํ˜ธ์ถœ"๊ณผ ๊ฐ™์€ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ›„ํฌ๋Š” ํ•จ์ˆ˜ ๊ตฌ์„ฑ ์š”์†Œ์˜ ๋ณธ๋ฌธ ๋‚ด๋ถ€์—์„œ๋งŒ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค."

๋‹ค์Œ์€ ์ด ์˜ค๋ฅ˜๋ฅผ ์ผ์œผํ‚ค๋Š” ์ฝ”๋“œ์˜ ์˜ˆ์ž…๋‹ˆ๋‹ค.

import React, { Component }  from "react";
import AddIcon from "@material-ui/icons/Add";
import { Fab } from "@material-ui/core";
import { makeStyles } from "@material-ui/core/styles";

const useStyles = makeStyles(theme => ({
   fab: {
      margin: theme.spacing(1),
   },
}));

const classes = useStyles();

export default class SampleComponent extends Component<{}, {}> {
   public render() {
      return (
         <div>
            <Fab color="primary" aria-label="Add" className={classes.fab}><AddIcon /></Fab>
         </div>
       );
   }
}

์šฐ๋ฆฌ๊ฐ€ ํ•ด์•ผ ํ•  ์ผ์€ useStyles ์ฝ”๋“œ๋ฅผ ํ•จ์ˆ˜๋กœ ๊ฐ์‹ธ๊ณ  ๊ทธ๊ฒƒ์„ ์‚ฌ์šฉํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ๋ฐ”๊พธ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด

const SampleFab = () => {
   const classes = useStyles();
   return <Fab color="primary" aria-label="Add" className={classes.fab}><AddIcon /></Fab>;
}

export default class SampleComponent extends Component<{}, {}> {
   public render() {
      return (
         <div>
            <SampleFab />
         </div>
      );
   }
}

์ด๊ฒƒ์€ ๋˜ํ•œ ๊ฐ„๋‹จํ•œ ํ•จ์ˆ˜์—์„œ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

=> ์ด์ƒํ•˜์ง€๋งŒ ์ž˜ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ๋„์›€์ด ๋˜๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

lerna ๋˜๋Š” ์ €์™€ ๊ฐ™์€ monorepo๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ Webpack ํ•ด๊ฒฐ ๊ตฌ์„ฑ์— ์ด์™€ ๊ฐ™์€ sth๋ฅผ ์ถ”๊ฐ€ํ•˜์‹ญ์‹œ์˜ค.

...
    alias: {
      'react-dom': 'path/to/main-package/node_modules/react-dom',
      react: 'path/to/main-package/form/node_modules/react',
    }
...

Storybook์—์„œ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž ์ง€์ • Webpack ๊ตฌ์„ฑ์—๋„ ์ถ”๊ฐ€ํ•˜์‹ญ์‹œ์˜ค.

์ฐธ๊ณ ๋กœ, ์ด ๋ฐฉ๋ฒ•์œผ๋กœ ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜๋ฉด ์›นํŒฉ ๋นŒ๋“œ ๋ฒˆ๋“ค์—๋„ 2๊ฐ€์ง€ ๋ฒ„์ „์˜ ๋ฐ˜์‘์ด ์žˆ์Šต๋‹ˆ๋‹ค(์›นํŒฉ ๋ฒˆ๋“ค ๋ถ„์„๊ธฐ์™€ ๊ฐ™์€ sth๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฒ€์‚ฌ). ์ œ ๊ฒฝ์šฐ์—๋Š” ๋ฒˆ๋“ค๋ง๊ณผ ๋ชจ๋“  ๋„๊ตฌ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ณ„๋„์˜ ํŒจํ‚ค์ง€๊ฐ€ ๋ชจ๋…ธ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์— ์žˆ์œผ๋ฏ€๋กœ ๋ณ„์นญ์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

lerna ๋˜๋Š” ์ €์™€ ๊ฐ™์€ monorepo๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ Webpack ํ•ด๊ฒฐ ๊ตฌ์„ฑ์— ์ด์™€ ๊ฐ™์€ sth๋ฅผ ์ถ”๊ฐ€ํ•˜์‹ญ์‹œ์˜ค.

...
    alias: {
      'react-dom': 'path/to/main-package/node_modules/react-dom',
      react: 'path/to/main-package/form/node_modules/react',
  }
...

Storybook์—์„œ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž ์ง€์ • Webpack ๊ตฌ์„ฑ์—๋„ ์ถ”๊ฐ€ํ•˜์‹ญ์‹œ์˜ค.

์ฐธ๊ณ ๋กœ, ์ด ๋ฐฉ๋ฒ•์œผ๋กœ ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜๋ฉด ์›นํŒฉ ๋นŒ๋“œ ๋ฒˆ๋“ค์—๋„ 2๊ฐ€์ง€ ๋ฒ„์ „์˜ ๋ฐ˜์‘์ด ์žˆ์Šต๋‹ˆ๋‹ค(์›นํŒฉ ๋ฒˆ๋“ค ๋ถ„์„๊ธฐ์™€ ๊ฐ™์€ sth๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฒ€์‚ฌ). ์ œ ๊ฒฝ์šฐ์—๋Š” ๋ฒˆ๋“ค๋ง๊ณผ ๋ชจ๋“  ๋„๊ตฌ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ณ„๋„์˜ ํŒจํ‚ค์ง€๊ฐ€ ๋ชจ๋…ธ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์— ์žˆ์œผ๋ฏ€๋กœ ๋ณ„์นญ์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์ œ ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. docs ๋‹ค๋ฅธ React๋ฅผ ์„ค์น˜ํ–ˆ๋Š”๋ฐ ์ด ๋•Œ๋ฌธ์— ์ด ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

์ €๋Š” ์•„์ฃผ ๊ธฐ๋ณธ์ ์ธ ๋ฐฉ์‹์œผ๋กœ hook์„ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

import React, {useState} from 'react'
import ReactDOM from 'react-dom'

function App() {
const [number, setNumber] = useState(0);
const increase = () => {
    setNumber(number+1);
}
return <div>
<span>Counting: {number}</span>
<button onClick={increase} >Increase</button>
</div>
}

const selector = document.getElementById('app');

ReactDOM.render(<App />, selector);

๋‚˜๋Š” ์˜ค๋ฅ˜๋ฅผ ์–ป๋Š”๋‹ค
Hooks can only be called inside of the body of a function component.
์Šค์ผ€์น˜์—์„œ ๋ฐ˜์‘ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŽ์ด ์ƒ์„ฑํ–ˆ์ง€๋งŒ ์ด์ „์—๋Š” ์ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.
ํ›„ํฌ์˜ ๋ชจ๋“  ๊ทœ์น™์„ ์ดํ•ดํ•ฉ๋‹ˆ๋‹ค.
๋””๋ฒ„๊น…ํ•˜๊ณ  ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด 12์‹œ๊ฐ„ ๋™์•ˆ ์‹œ๊ฐ„์„ โ€‹โ€‹ํ• ์• ํ•˜์ง€๋งŒ ๋ถˆํ–‰ํžˆ๋„ ๊ทธ๋Ÿด ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ ์ž˜ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค.

// Add this in node_modules/react-dom/index.js
window.React1 = require('react');

// Add this in your component file
require('react-dom');
window.React2 = require('react');
console.log(window.React1 === window.React2); // I get true

React ๋ฐ React DOM ๋ฒ„์ „์ด ์ผ์น˜ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
React ๋ฒ„์ „์„ ๋ณ€๊ฒฝํ•˜์—ฌ ๋™์ผํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.

๋‚ด ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๊ธฐ๋Šฅ์  ๊ตฌ์„ฑ ์š”์†Œ์—์„œ classfull ๊ตฌ์„ฑ ์š”์†Œ๋กœ ๋ณ€๊ฒฝํ•˜๋ ค๊ณ  ํ•  ๋•Œ ์ด ๋ฌธ์ œ์— ์ง๋ฉดํ–ˆ์œผ๋ฉฐ ์ด ์˜ค๋ฅ˜๋„ ๋ฐœ์ƒํ•˜๋ฏ€๋กœ ์ด ์˜ค๋ฅ˜์— ๋Œ€ํ•œ ํ•ด๊ฒฐ์ฑ…์ด ์—ฌ๊ธฐ์— ์žˆ์Šต๋‹ˆ๋‹ค. ๊ท€ํ•˜์˜ ๊ฒฝ์šฐ์—๋„ ๋„์›€์ด ๋˜๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

์ด ๊ฒฝ์šฐ ๊ณ ์ฐจ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•ด๋ณด์‹ญ์‹œ์˜ค.

'๋ฐ˜์‘'์—์„œ React ๊ฐ€์ ธ์˜ค๊ธฐ;
'prop-types'์—์„œ PropType์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
'@material-ui/styles'์—์„œ { withStyles } ๊ฐ€์ ธ์˜ค๊ธฐ;
'@material-ui/core/Button'์—์„œ ๋ฒ„ํŠผ ๊ฐ€์ ธ์˜ค๊ธฐ;

const ์Šคํƒ€์ผ = ํ…Œ๋งˆ => ({
๋ฃจํŠธ: {
๋ฐฐ๊ฒฝ: '์„ ํ˜• ๊ทธ๋ผ๋ฐ์ด์…˜(45deg, #FE6B8B 30%, #FF8E53 90%)',
ํ…Œ๋‘๋ฆฌ: 0,
ํ…Œ๋‘๋ฆฌ ๋ฐ˜๊ฒฝ: 3,
boxShadow: '0 3px 5px 2px rgba(255, 105, 135, .3)',
์ƒ‰์ƒ: 'ํฐ์ƒ‰',
ํ‚ค: 48,
ํŒจ๋”ฉ: '0 30px',
},
});

ํด๋ž˜์Šค HigherOrderComponent๋Š” React.Component๋ฅผ ํ™•์žฅํ•ฉ๋‹ˆ๋‹ค. {

์„ธ์šฐ๋‹ค(){
const { ํด๋ž˜์Šค } = this.props;
๋ฐ˜ํ’ˆ (

HigherOrderComponent.propTypes = {
ํด๋ž˜์Šค: PropTypes.object.isRequired,
};

๊ธฐ๋ณธ ๋‚ด๋ณด๋‚ด๊ธฐ withStyles(styles)(HigherOrderComponent);

๋‚˜๋Š” React์™€ FWIW๋ฅผ ๋ฐฐ์šฐ๊ณ  ์žˆ์œผ๋ฉฐ ๋‹ค์Œ๊ณผ ๊ฐ™์ด App()์„ ๋ Œ๋”๋งํ•˜๋ ค๊ณ  ํ•  ๋•Œ๋„ ๋ฌธ์ œ๋ฅผ ๋ณด์•˜์Šต๋‹ˆ๋‹ค.

ReactDOM.render(App(), $("#root"));

๋‚ด ์ถ”๋ก ์€ <App /> ๊ฐ€ App ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์‚ฝ์ž…ํ•˜๊ธฐ ์œ„ํ•ด ์•”์‹œ์ ์œผ๋กœ ๋ฏธ๋‹ˆ ํ…œํ”Œ๋ฆฟ์„ ์ƒ์„ฑํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋ฏ€๋กœ ๋Œ€์‹  JSX ํ…œํ”Œ๋ฆฟ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” App์„ ์ง์ ‘ ํ˜ธ์ถœํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ setState()๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์‹œ์ž‘ํ•˜๋ฉด ์•ž์„œ ์–ธ๊ธ‰ํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. <App /> ๋ณ€๊ฒฝํ•˜๋ฉด ์ด ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

ReactDOM.render(<App />, $("#root"));

์ฐธ๊ณ : $๋Š” ๋„์šฐ๋ฏธ const $ = sel => document.querySelector(sel); ๋ฟ์ž…๋‹ˆ๋‹ค.

์ œ์•ˆ๋œ ๋ชจ๋“  ์ˆ˜์ • ์‚ฌํ•ญ์„ ํ•ด๊ฒฐํ•œ ํ›„์—๋„ ์ด ์˜ค๋ฅ˜๊ฐ€ ๊ณ„์† ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ์šฐ ํ”„๋กœ์ ํŠธ๋ฅผ ์‹คํ–‰ํ•  ๋•Œ ์˜ฌ๋ฐ”๋ฅธ ๋””๋ ‰ํ„ฐ๋ฆฌ ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š”์ง€ ๋‹ค์‹œ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค. ๋‚˜๋Š” ๋‚ด PC์— ๊ณ„์† ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ๊ณ  ์ด๊ฒƒ์ด ๊ทธ ์ด์œ ์ž„์„ ๋ฐœ๊ฒฌํ–ˆ์Šต๋‹ˆ๋‹ค.

https://stackoverflow.com/questions/58365151/hooks-error-invalid-hook-call-using-nextjs-or-reactjs-on-windows

react-dom ๋ฅผ 16.10.2 ๋ฒ„์ „์œผ๋กœ ์—…๋ฐ์ดํŠธํ•œ ํ›„ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•  ๋•Œ ํ•ด๋‹น ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. enzyme-adapter-react-16 ๋ฅผ 1.15.1 ๋ฒ„์ „์œผ๋กœ ์—…๊ทธ๋ ˆ์ด๋“œํ•˜๋ฉด ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์˜ค, ๋‚˜๋Š” ๋ฐ˜์‘ v16.10.2์— ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ด๋ฏธ ๋ช‡ ์‹œ๊ฐ„ ๋™์•ˆ ์ด๊ฒƒ๊ณผ ์‹ธ์šฐ๋ ค๊ณ  ๋…ธ๋ ฅํ–ˆ์ง€๋งŒ ๋” ์ค‘์š”ํ•œ ์ผ์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋” ์ด์ƒ ์ด ์ „ํˆฌ๋ฅผ ๊ณ„์†ํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ react-final-form 4.1.0์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ์ขŒ์ ˆ๊ฐ.

์ด๊ฒƒ์€ ๋‚ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.

https://github.com/facebook/create-react-app/issues/7676#issuecomment -531543375

๋‚˜๋Š” ์˜์กด์„ฑ์„ ์—…๊ทธ๋ ˆ์ด๋“œํ•œ ์งํ›„์— ๊ฐ™์€ ๋ฌธ์ œ๋ฅผ ๊ฒช์—ˆ๊ณ  ์ด๊ฒƒ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ช‡ ์‹œ๊ฐ„์„ ๋ณด๋ƒˆ์Šต๋‹ˆ๋‹ค.

npm ls react ๋Š” ๋‹ค์Œ์„ ๋ณด์—ฌ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

โ”œโ”€โ”ฌ @storybook/[email protected]
โ”‚ โ””โ”€โ”€ [email protected]
โ””โ”€โ”€ [email protected]

react ๋ฐ react-dom ๋ฅผ 16.9.0 ํ•˜๋ฉด ์ผ์‹œ์ ์ธ ์ข…์†์„ฑ์ด ์‚ฌ๋ผ์ง€๋ฏ€๋กœ ์˜ค๋ฅ˜๊ฐ€ ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ฒ˜์Œ์— ์ด๊ฒƒ์ด 16.8.6 ์—์„œ๋„ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•„์ฐจ๋ฆด ๋•Œ๊นŒ์ง€ 16.10.0 ํšŒ๊ท€๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค(์ผ์‹œ์ ์ธ ์ข…์†์„ฑ์ด ๋ถ„๋ช…ํžˆ ๊ณ ์ •๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์—).

yarn์˜ resolutions ํ•„๋“œ ๋Š” ํŠนํžˆ lerna monorepos์—์„œ ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜๊ธฐ๊ฐ€ ์•ฝ๊ฐ„ ๊นŒ๋‹ค๋กญ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ React์˜ ๋‘ ๋ฒˆ์งธ ์‚ฌ๋ณธ์„ ์ œ๊ฑฐํ•˜๋Š” ๊ฒƒ์ด ํŠธ๋ฆญ์„ ์ˆ˜ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๊ฒƒ์ด ๋ฒˆ๋“ค๋กœ ์–ด๋–ป๊ฒŒ ๋๋‚ฌ๋Š”์ง€ ์—ฌ์ „ํžˆ ๋ชจ๋ฅด์ง€๋งŒ ๊ทธ๊ฒƒ์ด ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚จ ์›์ธ์ž…๋‹ˆ๋‹ค.

์ง€๊ธˆ์€ @storybook/addon-info ์™„์ „ํžˆ ์‚ญ์ œํ•˜๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. React 0.14์— ๋Œ€ํ•œ ์ข…์†์„ฑ์ด ์žˆ๋Š” ๋‹ค๋ฅธ ์ข…์†์„ฑ์—๋„ ์˜์กดํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค(์˜ˆ). ํ•˜์ง€๋งŒ ์•„์ง ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€๋Š” ์•Š์•˜์Šต๋‹ˆ๋‹ค.

ํŽธ์ง‘: ๊ธฐ๋ก์„ ์œ„ํ•ด, ๋‚˜๋Š” resolutions ๋ฅผ ์ค‘๋ณต์„ ์ œ๊ฑฐํ•  ์ˆ˜ ์—†์—ˆ๊ณ  React ์‚ฌ๋ณธ์ด ๋ฒˆ๋“ค์— ๋“ค์–ด์™”๋‹ค๋Š” ๊ฒƒ์„ ๋ฏฟ๊ธฐ๋ฅผ ๊ฑฐ๋ถ€ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๊ฒƒ์— ๋Œ€ํ•ด ์•ฝ 4์‹œ๊ฐ„์„ ๋ณด๋ƒˆ์Šต๋‹ˆ๋‹ค. ๋‚˜์ฒ˜๋Ÿผ ํ•˜์ง€๋งˆ. ์ผ์‹œ์ ์ธ React๋ฅผ ์ค‘๋ณต ์ œ๊ฑฐํ•˜์‹ญ์‹œ์˜ค.

์ด๊ฒƒ์€ HOC ๋‚ด๋ถ€์—์„œ ํ›„ํฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

const HOC = Component => {
  return (props) => {
    const [val] = useState();
    return <div>{val}</div>
  }
}

๋‚˜๋Š” ๋˜ํ•œ ๊ฐ™์€ ๋ฌธ์ œ์— ์ง๋ฉดํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

https://stackoverflow.com/questions/59173968/react-error-hooks-can-only-be-called-inside-the-body-of-a-function-component?noredirect=1#comment104570333_59173968

์ €๋ฅผ ์•ˆ๋‚ดํ•ด ์ฃผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?

@Neeraj-swarnkar ์—ฌ์ „ํžˆ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ตœ์†Œํ•œ์˜ ์˜ˆ์ œ๋กœ ๋ฌธ์ œ๋ฅผ ์ค„์ด์‹ญ์‹œ์˜ค.

๊ฑฐ์˜ ํ•ญ์ƒ ๋‘ ๊ฐ€์ง€ ๋ฌธ์ œ ์ค‘ ํ•˜๋‚˜๋กœ ์š”์•ฝ๋ฉ๋‹ˆ๋‹ค.

  • ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์ „๋‹ฌํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” ๋ Œ๋”๋ง ์†Œํ’ˆ์ž…๋‹ˆ๋‹ค(์ฆ‰, "๊ตฌ์„ฑ ์š”์†Œ"๋Š” ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์•„๋‹ˆ๋ผ ํ•จ์ˆ˜๋กœ ํ˜ธ์ถœ๋จ).
  • ์ผ์‹œ์ ์ธ ์ข…์†์„ฑ์„ ํ†ตํ•ด ์–ด๋–ป๊ฒŒ๋“  ๋‘˜ ์ด์ƒ์˜ React ๋ฒ„์ „์„ ๋ฒˆ๋“ค๋กœ ๋ฌถ์—ˆ๊ณ  ์„œ๋กœ์˜ ๋ฐœ๊ฐ€๋ฝ์„ ๋ฐŸ์Šต๋‹ˆ๋‹ค.

์Šฌํ”„๊ฒŒ๋„ ์ด ๊ฒฝ์šฐ ์Šคํƒ ์ถ”์ ์€ ์˜คํ•ด์˜ ์†Œ์ง€๊ฐ€ ์žˆ๋Š” ๊ฒฝํ–ฅ์ด ์žˆ์ง€๋งŒ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์˜ฌ๋ฐ”๋ฅธ ์•„์ด๋””์–ด๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

@Neeraj-swarnkar ์—ฌ์ „ํžˆ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ตœ์†Œํ•œ์˜ ์˜ˆ์ œ๋กœ ๋ฌธ์ œ๋ฅผ ์ค„์ด์‹ญ์‹œ์˜ค.

๊ฑฐ์˜ ํ•ญ์ƒ ๋‘ ๊ฐ€์ง€ ๋ฌธ์ œ ์ค‘ ํ•˜๋‚˜๋กœ ์š”์•ฝ๋ฉ๋‹ˆ๋‹ค.

  • ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์ „๋‹ฌํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” ๋ Œ๋”๋ง ์†Œํ’ˆ์ž…๋‹ˆ๋‹ค(์ฆ‰, "๊ตฌ์„ฑ ์š”์†Œ"๋Š” ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์•„๋‹ˆ๋ผ ํ•จ์ˆ˜๋กœ ํ˜ธ์ถœ๋จ).
  • ์ผ์‹œ์ ์ธ ์ข…์†์„ฑ์„ ํ†ตํ•ด ์–ด๋–ป๊ฒŒ๋“  ๋‘˜ ์ด์ƒ์˜ React ๋ฒ„์ „์„ ๋ฒˆ๋“ค๋กœ ๋ฌถ์—ˆ๊ณ  ์„œ๋กœ์˜ ๋ฐœ๊ฐ€๋ฝ์„ ๋ฐŸ์Šต๋‹ˆ๋‹ค.

์Šฌํ”„๊ฒŒ๋„ ์ด ๊ฒฝ์šฐ ์Šคํƒ ์ถ”์ ์€ ์˜คํ•ด์˜ ์†Œ์ง€๊ฐ€ ์žˆ๋Š” ๊ฒฝํ–ฅ์ด ์žˆ์ง€๋งŒ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์˜ฌ๋ฐ”๋ฅธ ์•„์ด๋””์–ด๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

ํฌ์ธํ„ฐ ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๋” ์ž˜ํ•  ์ˆ˜ ์žˆ๋Š” ์ž‘์—…์ด๋‚˜ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ•ฉ๋‹ˆ๊นŒ?

@Neeraj-swarnkar๋Š” ์—ฌ์ „ํžˆ ๋™์ผํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ตœ์†Œํ•œ์˜ ์˜ˆ์ œ๊ฐ€ ์žˆ์„ ๋•Œ๊นŒ์ง€ ์ฝ”๋“œ๋ฅผ ์ฃผ์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ์ž๋ฆฌ ํ‘œ์‹œ์ž๋กœ ๊ต์ฒดํ•ด ๋ด…๋‹ˆ๋‹ค.

๋™๋ฃŒ๋‚˜ ๋™๋ฃŒ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ ์ด ๋ฌธ์ œ๋ฅผ ๋””๋ฒ„๊น…ํ•˜๋Š” ๋ฐ ๋„์›€์„ ์š”์ฒญํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ง€์—ญ ๋ชจ์ž„, ์Šคํ„ฐ๋”” ๊ทธ๋ฃน ๋˜๋Š” ๊ทผ๋ฌด ์‹œ๊ฐ„์„ ์—ฌ๋Š” ํšŒ์‚ฌ๋ฅผ ์ฐพ์•„ ๋„์›€์„ ์š”์ฒญํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ๋ชจ๋“  ๋ฐฉ๋ฒ•์ด ์‹คํŒจํ•˜๋ฉด ๋‹ค๋ฅธ ์‚ฌ๋žŒ์„ ๊ณ ์šฉํ•˜์—ฌ ์กฐ์‚ฌํ•˜์‹ญ์‹œ์˜ค.

๋‚ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ create-react-app์€ ์„œ๋กœ ์ƒ๋Œ€์ ์ด๋ฉฐ npm ๋งํฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ถ€๋ถ„์„ ์ฝ์—ˆ์Šต๋‹ˆ๊นŒ?

https://reactjs.org/warnings/invalid-hook-call-warning.html#duplicate - ๋ฐ˜์‘

link ์›Œํฌํ”Œ๋กœ๋ฅผ ์ง์ ‘ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

Screen Shot 2019-04-04 at 09 28 47

์ด๊ฒƒ์€ ๋งค๋ ฅ์ฒ˜๋Ÿผ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค!

๋‚ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ create-react-app์€ ์„œ๋กœ ์ƒ๋Œ€์ ์ด๋ฉฐ npm ๋งํฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ถ€๋ถ„์„ ์ฝ์—ˆ์Šต๋‹ˆ๊นŒ?

https://reactjs.org/warnings/invalid-hook-call-warning.html#duplicate - ๋ฐ˜์‘

link ์›Œํฌํ”Œ๋กœ๋ฅผ ์ง์ ‘ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

Screen Shot 2019-04-04 at 09 28 47

์ด๊ฒƒ์€ ๋งค๋ ฅ์ฒ˜๋Ÿผ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค!

์–ด์ œ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ๋Š”๋ฐ ๋ฐ˜๋‚˜์ ˆ ๋™์•ˆ ๊ท€์ฐฎ๊ฒŒ ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ด๊ฒƒ์„ ๋ณ€ํ™”๋กœ ํ•ด๊ฒฐํ•œ๋‹ค

                <Route key={index} render={route.component} path={route.path} />

์—๊ฒŒ

                <Route key={index} component={route.component} path={route.path} />

ํ•˜์ง€๋งŒ ๋‚˜๋Š” ์™œ ๊ทธ๋Ÿฐ์ง€ ๋ชจ๋ฅด๊ฒ ๋‹ค :(

๊ทธ๊ฒƒ์€ ๋‚˜๋ฅผ ์œ„ํ•ด ์ž‘๋™ํ•˜์ง€๋งŒ ๋‚˜๋„ ์ด์œ ๋ฅผ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋จธํ‹ฐ๋ฆฌ์–ผ UI์—์„œ HOC + makeStyles ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์—ฌ๊ธฐ์— ์˜ค๋Š” ๊ฒฝ์šฐ,
์—ฌ๊ธฐ ๋‹น์‹ ์˜ ๋Œ€๋‹ต์ด ์žˆ์Šต๋‹ˆ๋‹ค: https://stackoverflow.com/questions/56329992/invalid-hook-call-hooks-can-only-be-call-inside-of-the-body-of-a-function-com

๋‚˜๋Š” ์ด๊ฒƒ์„ ๋ณ€ํ™”๋กœ ํ•ด๊ฒฐํ•œ๋‹ค

                <Route key={index} render={route.component} path={route.path} />

์—๊ฒŒ

                <Route key={index} component={route.component} path={route.path} />

ํ•˜์ง€๋งŒ ๋‚˜๋Š” ์™œ ๊ทธ๋Ÿฐ์ง€ ๋ชจ๋ฅด๊ฒ ๋‹ค :(

๊ทธ๊ฒƒ์€ ๋‚˜๋ฅผ ์œ„ํ•ด ์ž‘๋™ํ•˜์ง€๋งŒ ๋‚˜๋„ ์ด์œ ๋ฅผ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค.

"component" ์†Œํ’ˆ์€ React ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ์˜ˆ์ƒํ•˜๊ณ  "render" ์†Œํ’ˆ์€ ํ•จ์ˆ˜๋กœ ํ˜ธ์ถœ๋  ํ•จ์ˆ˜๋ฅผ ์˜ˆ์ƒํ•ฉ๋‹ˆ๋‹ค. ํ•จ์ˆ˜ ๊ตฌ์„ฑ ์š”์†Œ๋Š” ๊ตฌ์„ฑ ์š”์†Œ์ด๋ฉฐ ํ›„ํฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ์ž‘๋™ํ•˜์ง€ ์•Š์œผ๋ฉฐ ์ผ๋ฐ˜์ ์œผ๋กœ ์ข‹์€ ์ƒ๊ฐ์ด ์•„๋‹™๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ด๊ฒƒ์„ ๋ณ€ํ™”๋กœ ํ•ด๊ฒฐํ•œ๋‹ค

                <Route key={index} render={route.component} path={route.path} />

์—๊ฒŒ

                <Route key={index} component={route.component} path={route.path} />

ํ•˜์ง€๋งŒ ๋‚˜๋Š” ์™œ ๊ทธ๋Ÿฐ์ง€ ๋ชจ๋ฅด๊ฒ ๋‹ค :(

๊ณ ๋งˆ์›Œ, ์นœ์• ํ•˜๋Š”, ๊ทธ๊ฒƒ์€ ๋‚˜๋ฅผ ์œ„ํ•ด ์ผํ–ˆ์Šต๋‹ˆ๋‹ค. ์—ฌ์ „ํžˆ, ๋‚˜๋Š” ํ˜ผ๋ž€ ์Šค๋Ÿฝ์Šต๋‹ˆ๋‹ค. ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

@jaisonjjames ๋‚ด ๋‹ต์žฅ์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค. ์ฐจ์ด์ ์„ ์ดํ•ดํ•˜๋Š” ๋ฐ ๋ฌธ์ œ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ StackOverflow์— ๋Œ€ํ•œ ๋ช‡ ๊ฐ€์ง€ ์„ค๋ช…์ด ์žˆ์Šต๋‹ˆ๋‹ค. https://stackoverflow.com/questions/48150567/react-router-difference-between-component-and-render

์ œ ๊ฒฝ์šฐ์—๋Š” react-router์˜ hook์ธ component ์™ธ๋ถ€์— let match = useRouteMatch("/user/:id"); ๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ ์˜ค๋ฅ˜๊ฐ€ ๋งž์Šต๋‹ˆ๋‹ค.

~์—์„œ

์—๊ฒŒ

~์—์„œ

<Route path="/component" component={myComponent} />

์—๊ฒŒ

<Route path="/component"><myComponent /></Route>

์ œ ๊ฒฝ์šฐ์—๋Š” React ๋ฒ„์ „์ด ๋‹ค๋ฅธ window ๊ฐ์ฒด์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ฐธ์กฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ฒฐ๊ตญ ๊ทธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ react ๋ฐ react-dom ์ฐธ์กฐํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋˜๋Š” webpack์˜ externals ๊ตฌ์„ฑ ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์˜ค๋ฅ˜๋ฅผ ์ œ๊ฑฐํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ์ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ์ด์œ ๋Š” You might have more than one copy of React in the same app ๋•Œ๋ฌธ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์ œ ๊ฒฝ์šฐ์—๋Š” React ๋ฒ„์ „์ด ๋‹ค๋ฅธ window ๊ฐ์ฒด์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ฐธ์กฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ฒฐ๊ตญ ๊ทธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ react ๋ฐ react-dom ์ฐธ์กฐํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋˜๋Š” webpack์˜ externals ๊ตฌ์„ฑ ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์˜ค๋ฅ˜๋ฅผ ์ œ๊ฑฐํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ์ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ์ด์œ ๋Š” You might have more than one copy of React in the same app ๋•Œ๋ฌธ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

@AbreezaSaleem ์–ด๋–ป๊ฒŒ ์ด๊ฒƒ์„ ๋‹ฌ์„ฑ ํ–ˆ์Šต๋‹ˆ๊นŒ? react ๊ฐ€ ์™ธ๋ถ€๋กœ ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์–ด๋–ป๊ฒŒ๋“  ๋‹ค๋ฅธ ๋ฒ„์ „์œผ๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค... externals: [ 'react' ] , externals: { react: 'react' } ๋ฐ externals: { react: 'React' } , ๊ฒฐ๊ณผ๋Š” ํ•ญ์ƒ ๋™์ผํ•ฉ๋‹ˆ๋‹ค...

๋‚˜๋Š” ์ด๊ฒƒ์„ ์žฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์†Œ ์ตœ์†Œํ•œ์˜ ์˜ˆ์ œ๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ์ œ ๊ฒฝ์šฐ์—๋Š” electron-webpack ๋ฐ react-dropzone์ด ์žˆ๋Š” ElectronJS์—์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

https://github.com/JuanIrache/dropzone-test

๋™์ผํ•œ/๋‹จ์ผ ๋ฐ˜์‘ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ๋ฉ‹์ง„ ์›นํŒฉ ๊ตฌ์„ฑ ์†”๋ฃจ์…˜์ด ์žˆ์Šต๋‹ˆ๋‹ค. 'to be npm ๋ชจ๋“ˆ'์„ ์‚ฌ์šฉํ•˜๋Š” ์•ฑ ๋‚ด๋ถ€์— ํ•ด์ƒ๋„๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

webpack.config.js ๋‚ด๋ถ€์—์„œ ๋ฐ˜์‘์— ๋ณ„์นญ์„ ์ถ”๊ฐ€ํ•˜์—ฌ ํ•˜๋‚˜์˜ ๋ฐ˜์‘ ์ธ์Šคํ„ด์Šค๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

module.exports = {
resolve: {
    alias: {
      react: path.resolve('./node_modules/react')
    }
  },
 // other webpack settings
}

์˜ค๋Š˜ ๋ฐฐ์šด(lerned?) lerna๋Š” ์ด ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

lerna ์‚ฌ์šฉ์ž๋Š” ๊ตฌ์„ฑ ์š”์†Œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๊ฐœ๋ฐœํ•˜๋Š” ๊ฒฝ์šฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์ข…์†์„ฑ์— ๋ฐ˜์‘์„ ํฌํ•จํ•˜์ง€ ๋ง๊ณ  peerDependencies์— ๋„ฃ๊ณ  @types/react๋ฅผ devDependencies์— ๋„ฃ์Šต๋‹ˆ๋‹ค.

lerna๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋“  ์‚ฌ๋žŒ์€ ์ฝ”๋“œ๊ฐ€ ๋‹ค๋ฅธ ํŒจํ‚ค์ง€์˜ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์ฐธ์กฐํ•˜๋Š” ํ•œ ํŒจํ‚ค์ง€์—์„œ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•  ๋•Œ ์ด ๋ฌธ์ œ๋ฅผ ์ ‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๊ฒฝ์šฐ์— ์šฐ๋ฆฌ๊ฐ€ ๊ฒฝํ—˜ํ•œ ๋ฌธ์ œ๋Š” ์‚ฌ์–‘์—์„œ ์ž„ํฌํŠธ๋˜๋Š” ๋ฐ˜์‘๊ณผ Material UI์˜ ํ›„ํฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌํ˜„๋˜๋Š” ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ ์ž…๋‹ˆ๋‹ค.

๋ฐ˜์‘์€ ๋‚ด๋ถ€์ ์œผ๋กœ ReactCurrentDispatcher.current ๋ณ€์ˆ˜์˜ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์ด๋ฉฐ ์ด๋Š” ๊ฒฐ๊ตญ ๋ฐ˜์‘์˜ ํ•œ ์ธ์Šคํ„ด์Šค์—์„œ ์„ค์ •๋˜์ง€๋งŒ ๋ฐ˜์‘์˜ ๋‹ค๋ฅธ ์ธ์Šคํ„ด์Šค์—์„œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋น„์–ด ์žˆ์œผ๋ฉด Invalid hook call ... ๋˜์ง‘๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” ์ด๋ฏธ Craco ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋นŒ๋“œ ์‹œ Create React App์˜ webpack ๊ตฌ์„ฑ์„ ์žฌ์ •์˜ํ•˜๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

  webpack: {
    alias: {
      react: path.resolve(__dirname, './node_modules/react'),
    },
  },

๊ทธ๋Ÿฌ๋‚˜ ์ด ์›นํŒฉ ์žฌ์ •์˜๋Š” ๋นŒ๋“œ ์‹œ์—๋งŒ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•  ๋•Œ ์ฝ”๋“œ๋Š” ๋นŒ๋“œ๋˜์ง€ ์•Š๊ณ  ์†Œ์Šค์—์„œ ์ธ์Šคํ„ด์Šคํ™”๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์šฐ๋ฆฌ์˜ ์†”๋ฃจ์…˜์€ craco.config.js ์—์„œ CracoAlias ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ ์ค‘ ๋ฐ˜์‘ ๊ฒฝ๋กœ:

  plugins: [
    {
      plugin: CracoAlias,
      options: {
        source: 'options',
        baseUrl: './',
        aliases: {
          // We need to alias react to the one installed in the desktop/node_modules
          // in order to solve the error "hooks can only be called inside the body of a function component"
          // which is encountered during desktop jest unit tests,
          // described at https://github.com/facebook/react/issues/13991
          // This is caused by two different instances of react being loaded:
          // * the first at packages/desktop/node_modules (for HostSignUpDownloadComponent.spec.js)
          // * the second at packages/components/node_modules (for packages/components/Modal)
          react: './node_modules/react',
        },
      },
    },
  ],
์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰