반응형
Type '{ auth: any; }' is not assignable to type 'IntrinsicAttributes & AuthProps'.
Property 'auth' does not exist on type 'IntrinsicAttributes & AuthProps'.

 

react에서 typescript로 코드를 작성할 때, 하위 컴포넌트에 props를 전달할 때 발생하는 오류입니다.

javascript에서는 그냥 작성해도 문제가 되지 않지만, typescript에서 작성하게되면 규칙에 맞춰서 작성해 주어야합니다.

 

Main.tsx

export const Main = () => {

    const auth: any = useSelector<Record<string, number>>(state => state.reducer);

    return (
        <article>
            <MainHeader auth={auth} /> // 에러 발생 부분
        </article>
    );
};

 

MainHeader.tsx

export const MainHeader = (auth) => {
    console.log(auth);
};

 

 


 

해결방법 1

 

Main.tsx

export const Main = () => {

    const auth: any = useSelector<Record<string, number>>(state => state.reducer);

    return (
        <article>
            <MainHeader auth={auth} /> // 에러 발생 부분
        </article>
    );
};

 

MainHeader.tsx

interface AuthProps {
    state: {
        _id: string,
        createdAt: string,
        email: string,
        age: number,
        profile: string
    }
    type: string
}

export const MainHeader = ({ auth }: { auth: AuthProps }) => {
    console.log(auth);
}

 

 

해결방법 2

 

Main.tsx

export const Main = () => {

    const auth: any = useSelector<Record<string, number>>(state => state.reducer);

    return (
        <article>
            <MainHeader {...auth} />

            <div className={styles.main_contents}>
                <MainContents />
            </div>
        </article>
    );
};

 

MainHeader.tsx

interface AuthProps {
    state: {
        _id: string,
        createdAt: string,
        email: string,
        age: number,
        profile: string
    }
    type: string
}

export const MainHeader = ({...auth}: AuthProps) => {
    console.log(auth);
}
반응형
반응형

npm

$ npm install react-router-dom

 

App.js

import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';

import { RouterTest } from './components/login/RouterTest';

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/routerTest/*" element={<RouterTest />} />
      </Routes>
    </Router>
  );
}

export default App;

 

RouterTest.js

import React from 'react';
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
import { Router1 } from './Router1';
import { Router2 } from './Router2';

export const RouterTest = () => {
    return (
        <Routes>
            <Route path="test1" element={<Router1 />} />
            <Route path="test2" element={<Router2 />} />
        </Routes>
    );
};

 

반응형
반응형

Java, Android Studio, Xcode 등을 설치하지 않고 React Native앱을 실행시킬 수 있는 방법입니다.

 

expo 바로가기

watchman 바로가기

 

expo

expo는 앱등록을 하지 않고, react-native를 베이스로 iOS, Andorid, Web 등을 개발하고 빌드, 배포를 할 수 있도록 도와주는 프레임워크입니다.

 

watchman

watchman은 파일 모니터링 도구로, MacOS에만 지원됩니다. 파일을 모니터링하면서 변화가 생겼을 때, 특정 작업을 처리하는 용도로 사용됩니다.

:: MacOS에서만 지원되므로, 윈도우에서는 설치할 필요가 없습니다.

 

expo 시작하기

expo init App-Name

 

expo 로그인하기 ( 핸드폰과 VSCode 둘다 로그인을 해줍니다. )

 

VSCode

expo login

 

실행하기

$ npm start

npm start를 하고나서, 핸드폰에 expo를 켜보면 자신이 만든 App-Name의 파일이 생성된 것을 확인할 수 있습니다.

이후, 파일을 실행시키면 App.js의 화면을 확인할 수 있습니다.

반응형
반응형

npm

$ npm install redux
$ npm install react-redux
$ npm install redux-persist
$ npm install react-router-dom

 

index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

import { legacy_createStore as createStore } from 'redux';
import { Provider } from 'react-redux';
import rootReducer from './reducers';

import { persistStore } from 'redux-persist';
import { PersistGate } from 'redux-persist/integration/react';

const store = createStore(rootReducer);
const persistor = persistStore(store);

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <Provider store={store}>
    <PersistGate loading={null} persistor={persistor}>
      <App />
    </PersistGate>
  </Provider>
);

 

App.js

import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import Counter from "./components/Counter";
import Login from './components/Login';

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<Counter />} />
        <Route path="/login" element={<Login />} />
      </Routes>
    </Router>
  );
}

export default App;

 

components/Counter.js

import { useSelector, useDispatch } from "react-redux";
import { increaseCount, decreaseCount } from "../reducers/counter";


const Counter = () => {

    const dispatch = useDispatch();
    const { count } = useSelector(state => state.counter);

    const increase = () => {
        dispatch(increaseCount(count + 1));
    };
    const decrease = () => {
        dispatch(decreaseCount(count - 1));
    }

    return (
        <div>
            카운트{count} <br />
            <button onClick={increase}>증가</button>
            <button onClick={decrease}>감소</button>
        </div>
    );
};

export default Counter;

 

components/Login.js

import { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";
import { signIn } from "../reducers/auth";


const Login = () => {

    const dispatch = useDispatch();
    const navigation = useNavigate();
    const auth = useSelector(state => state.auth);

    const [formData, setFormData] = useState({ id: "", password: "" });
    const { id, password } = formData;

    const onChange = (e) => {
        const { target: { value, name } } = e;

        if (name === "id") setFormData({...formData, id: value});
        else if (name === "password") setFormData({...formData, password: value});
    };

    const onSubmit = (e) => {
        e.preventDefault();
        dispatch(signIn(formData))
        navigation("/login");
    }

    return (
        <>
            <form onSubmit={onSubmit}>
                <input type="text" name="id" value={id} onChange={onChange} />
                <input type="password" name="password" value={password} onChange={onChange} />
                <input type="submit" value="저장" />
            </form>

            {
                auth && <div>{auth.id} {auth.password}</div>
            }
        </>
    );
}

export default Login;

 

reducers/counter.js

export const INCREASE = "INCREASE";
export const DECREASE = "DECREASE";

export const increaseCount = count => ({ type: INCREASE, count });
export const decreaseCount = count => ({ type: DECREASE, count });

const initalState = {
    count: 0
};

export const counter = (state = initalState, action) => {
    switch(action.type) {
        case INCREASE :
            return {
                ...state,
                count: action.count
            };

        case DECREASE :
            return {
                ...state,
                count: action.count
            }

        default:
            return state;
    }
}

 

reducers/auth.js

export const AUTH = "SIGNIN";

export const signIn = (state) => ({ type: AUTH, state })

const initState = {
    id: "",
    password: ""
};

export const auth = (state = initState, action) => {
    console.log(action.state);
    switch(action.type) {
        case AUTH :
            return {
                ...state,
                id: action.state.id,
                password: action.state.password
            }

        default : 
            return state;

    }
}

 

reducers/index.js

import { combineReducers } from "redux";
import { counter } from "./counter";
import { auth } from "./auth";

import { persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage";
// import storageSession from 'redux-persist/lib/storage/session';

const persistConfig = {
    key: "root",
    storage: storage
}

const rootReducer = combineReducers({
    auth,
    counter,
})

export default persistReducer(persistConfig, rootReducer);

storage : 로컬스토리지

storageSession : 세션스토리지

:: 로컬스토리지는 새로고침, 브라우저 재실행해도 데이터가 사라지지 않고, 세션스토리지는 새로고침시에만 데이터가 사라지지 않습니다.

 

반응형
반응형

Redux

props를 사용하지 않고, 데이터를 전달할 수 있는 redux를 사용하는 기본방법입니다.

 

npm

$ npm install redux
$ npm install react-redux

 

js/reducer.js

const Reducer = ( currentState, action ) => {

    // currentState가 없을때 ( 최초상태 )
    if (currentState === undefined) {
        return {
            number: 1
        }
    }

    const newState = { ...currentState };

    if (action.type === "PLUS") {
        newState.number = action.num;
    }

    return newState;
};

export default Reducer;

 

App.js

import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';

import { legacy_createStore as createStore } from 'redux';
import { Provider } from 'react-redux';
import Reducer from './js/reducer';

import Main from './components/Main';

const store = createStore(Reducer);

function App() {

  return (
    <Router>
      <Provider store={store}>
        <Routes>
            <Route path="/" element={<Main />} />
        </Routes>
      </Provider>
    </Router>
  );
}

export default App;

 

 

components/Main.js

import { useState } from "react";
import { useDispatch, useSelector } from "react-redux";

const Main = () => {

    const dispatch = useDispatch();
    const [num, setNum] = useState(0);

    const number = useSelector(state => state.number);

    const onDispatch = () => {
        dispatch({ type: "PLUS", num: num})
    };
    
    return (
        <section>
            숫자 !!! : {number}

            <input type="number" value={num} onChange={(e) => setNum(e.target.value)} />
            <button onClick={onDispatch}>변환!</button>
        </section>
    );
}

 

반응형
반응형

절대 경로 설정하기

react에서./Main.js 이런 식으로 경로를 찾지 않고 component/Main.js처럼 항상 src에서 시작하는 절대경로를 설정해주는 방법입니다.

 

jsconfig.json

최상위 폴더 ( package.json과 같은 위치 )

{
    "compilerOptions": {
        "baseUrl": "src"
    },
    "include": ["src"]
}

이렇게 설정해주면 import를 해줄 때 절대 경로부터 시작해서 불러올 수 있습니다.

 

:: 오류가 날 경우 서버를 재실행시켜줍니다.

반응형
반응형

socket io를 사용해서 통신하는 방법입니다.

 

socket.io란 ?

NodeJS기반 '실시간' 웹 애플리케이션 지원 라이브러리입니다.

 

 

Client --> Server

io.on('connection', (socket) => {})

// connection : socket.io의 기본 이벤트이며, 사용자가 웹에 접속하면 자동으로 발생하는 이벤트입니다.
socket.on('message', (data) => {
    console.log('Data', data);
});

// 해당 클라이언트에서 메시지를 보내줍니다.

 

Server --> Client

io.emit('message', data);

// 서버가 현재 접속해있는 모든 클라이언트에게 이벤트를 전달해줍니다.
socket.emit('message', data);

// 서버에서 event를 발생시키는 함수입니다. 서버에서 이벤트를 발생시키면 클라이언트 페이지에 해당 이벤트에서 처리합니다. 해당 소켓을 통해 클라이언트에게 메시지를 전송합니다.

 

npm

// BackEnd
$ npm install socket.io

// FrontEnd
$ npm install socket.io-client

 

socket.js

import { io } from "socket.io-client";

const serverUrl = "http://localhost:4000/";

const socket = io(serverUrl);

export { socket };

 

App.js [ Front End ]

import { useEffect, useState } from "react";
import { socket } from "../../js/socket";

const App = () => {

    const [state, setState] = useState({name: '', message: ''});
    const [chat, setChat] = useState([]);

    useEffect(() => {
        socket.on('message', ({name, message}) => {
            setChat([...chat, {name, message}]);
        })
    }, [chat]);

    const textChange = (e) => {
        setState({...state, [e.target.name]: e.target.value});
    };

    const onSubmit = () => {
        const {name, message} = state;
        socket.emit('message', {name, message});
        setState({message: '', name})
    };

    return (
        <div>
            <input type="text" name="name" value={state.name} onChange={textChange} placeholder="이름입력" />
            <textarea value={state.message} name="message" onChange={textChange} />
            <button onClick={onSubmit}>내용입력</button>

            {
                chat.map((item, index) => (
                    <div key={index}>
                        <div>{item.name}</div>
                        <div>{item.message}</div>    
                    </div>
                ))
            }
        </div>
    );
};


export default App;

 

App.js [ Back End ]

const express = require('express');
const app = express();

const http = require('http');
const socket = require('socket.io');

const server = http.createServer(app);
const io = socket(server, {
	cors: {
		origin: "*",
		methods: ["GET", "POST"]
	}
});

const PORT = 4000;
server.listen(PORT, () => {
    const message = `
        [ Project ]
        Running PORT: localhost:${PORT}
    `;

    console.log(message);
});

io.on('connection', (socket) => {
    socket.on('message', ({name, message}) => {
        io.emit('message', ({name, message}));
    });
    console.log('연결 완료');
});

 

반응형
반응형

JSX에서 if문 사용하기

 

AND 연산자를 통한 처리

AND 연산자는 조건문이 true일때 동작하는 조건문입니다.

const Test2 = () => {

    const test = 1;
    // AND 연산자
    return (
        <div>
            {
                test === 1 && <div>AND연산자입니다.</div>
            }
        </div>
    );
};


export default Test2;

 

3항 연산자를 통한 처리

3항 연산자는 조건문이 true일때 좌측, false일때 우측에 있는 내용을 렌더링합니다.

()를 사용하면 줄바꿈을 사용할 수 있습니다.

const Test2 = () => {

    const test = 1;
    // 3항 연산자
    return (
        <div>
            {
                test === 1 ? <div>3항 연산자입니다.</div> : null
            }
            
            {
                test === 1 ? (
                    <div>3항 연산자입니다.</div>
                ) : null
            }
        </div>
    );
};


export default Test2;

 

if문을 통한 처리

함수를 정의하자마자 바로 호출하는 '즉시 실행 함수'를 통해 if문을 사용할 수 있습니다.

const Test2 = () => {

    const test = 1;
    // IF 문
    return (
        <div>
            {
                (function() {
                    if (test === 1) return (<div>IF 조건문입니다.</div>)
                    else if (test === 2) return (<div>IF 조건문입니다2.</div>)
                })()
            }
        </div>
    );
};


export default Test2;

 

화살표함수 버전

const Test2 = () => {

    const test = 1;
    // IF 문
    return (
        <div>
            {
                (() => {
                    if (test === 1) return (<div>화살표함수버전</div>)
                })()
            }
        </div>
    );
};


export default Test2;

 

 

 

:: 즉시 실행 함수의 형태

(function() {
    contents
})()
반응형
반응형

Warning: Cannot update a component (`Main`) while rendering a different component (`MainHeader`). To locate the bad setState() call inside `MainHeader`, follow the stack trace as described in

 

렌더링하는 동안 함수가 함수가 나와서 업데이트를 할수 없는 상황이 생긴거같습니다.

저의 경우에는 자식 컴포넌트에서 부모 컴포넌트로 넘겨주는 상황에서 발생했습니다.

 

상황

import { useState } from "react";

const Child = ({parentFn}) => {

    const [title, setTitle] = useState('');
    
    parentFn(title); // 오류 발생지점
    
    const onTitle = (e) => {
        setTitle(e.target.value);
    };

    return (
        <>
            <input type="text" value={title} onChange={onTitle} />
            
            parentFn 함수가 렌더링 중에 나와버려서 생기는 오류입니다.
        </>
    );
};


export default Child;

 

해결방법

import { useEffect, useState } from "react";

const Child = ({parentFn}) => {

    const [title, setTitle] = useState('');
    
    // 해결 위치
    useEffect(() => {
        parentFn(title);
    }, [title])
    
    const onTitle = (e) => {
        setTitle(e.target.value);
    };

    return (
        <>
            <input type="text" value={title} onChange={onTitle} />
        </>
    );
};


export default Child;

 

위 코드에서는 useEffect로 감싸주었습니다.

 

반응형
반응형

React 검색 기능 만들기

 

App.js

import { useEffect, useState } from "react";


const App = () => {

    const array = [
        {
            id: 0,
            title: '안녕',
            nick: 'shiro21'
        },
        {
            id: 1,
            title: '하세요',
            nick: 'asd'
        },
        {
            id: 2,
            title: '안녕히',
            nick: 'aaa'
        },
        {
            id: 3,
            title: '가세요',
            nick: 'bbb'
        },
    ];

    const [contents, setContents] = useState(array);
    const [search, setSearch] = useState('');

    useEffect(() => {
        const filter = array.filter((item) => {
            return item.title.toUpperCase().includes(search.toUpperCase());
        });

        setContents(filter);
        
    }, [search]);

    const titleChange = (e) => {
        setSearch(e.target.value);
    };
    console.log(contents);
    console.log(search);


    return (
        <>
            <input type="text" value={search} placeholder="타이틀을 입력해주세요." onChange={titleChange} />
        </>
    );
};


export default App;

위 코드는 includes함수로 배열에서 특정값을 포함하고 있는지 확인하고, filter() 메서드로 포함하고 있는 지정된 배열의 요소로 얕은 복사를 만들어줍니다.

:: toUpperCase() 메서드로 소문자를 대문자로 변환해서 소문자 대문자를 구분하지 않도록 해줍니다.

반응형

+ Recent posts