์ด ๊ธ์ "๋ฆฌ์กํธ๋ฅผ ๋ค๋ฃจ๋ ๊ธฐ์ (๊น๋ฏผ์ค, ๊ธธ๋ฒ)"์ ์์ฝํ ๊ธ์ ๋๋ค.
๋ง์ ์์ ์ฝ๋๋ค์ด ์ด ์ฑ ์์ ์ธ์ฉ๋์์ต๋๋ค.
์์ธํ๊ณ ์ ํํ ๋ด์ฉ์ ํ์ธํ์๋ ค๋ฉด ์ฑ ์ ์ง์ ๊ตฌ๋งคํด,
์ฝ์ด๋ณด์๊ธฐ๋ฅผ ์ถ์ฒ๋๋ฆฝ๋๋ค.
import styled from 'styled-components'
const Button = styled.button`
background: white;
color: black;
`
<Button>ํด๋ฆญ</Button>
import React from 'react'
export const Component extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
name: "",
age: "",
country: ""
};
}
componentDidUpdate(prevProps) {
if (this.props.userID !== prevProps.userID) {
this.fetchData(this.props.userID);
}
}
render() {
return <h1>Hello, World!!</h1>
}
}
useEffect(() => {
"ํ์ด์ง๊ฐ ๋ง์ดํธ ๋๊ฑฐ๋ ์
๋ฐ์ดํธ ๋ ์ดํ ์คํํ ๋ช
๋ น์ด";
}, []);
useEffect(() => {
return () => {
"์
๋ฐ์ดํธ๊ฐ ๋๊ธฐ ์ ์ด๋ ์ธ๋ง์ดํธ ๋๊ธฐ ์ ์คํํ ๋ช
๋ น์ด";
};
}, []);
hooks(() => {}, ["์ฌ๊ธฐ ์
๋ ฅํ ๋ณ์๋ค์ด ์
๋ฐ์ดํธ๋๋ฉด Hooks๊ฐ ์คํ"]);
// in Jinja Template
{% for example in examples %}
<div>
Hello, {{bank.name}} !!
</div>
{% endfor %}
// in React
{
examples.map(example=>{
<div>
Hello, {bank.name} !!
</div>
})
}
boolean ? true : false
background-color -> backgroundColor
<input>, <br>
๊ฐ์ด HTML Element๋ฅผ ๊ผญ ๋ซ์์ฃผ์ด์ผ ํจ{/_ ... _/}
ํํ๋ก ์จ์ผํจ: //
๋ก๋ ์ฃผ์ ์ฒ๋ฆฌ ๋ถ๊ฐconst [variable, control Function] = useHooks(( ) => { }, [variables watching at])
import React, { useState } from "react";
const Counter = () => {
const [value, setValue] = useState(0);
return (
<div>
<p>ํ์ฌ ๊ฐ์ {value}์
๋๋ค.</p>
<button
onClick={() => {
setValue(value + 1);
}}
>
+1
</button>
<button
onClick={() => {
setValue(value - 1);
}}
>
-1
</button>
</div>
);
};
import React, { useState, useEffect } from "react";
const Info = () => {
const [name, setName] = useState("");
const onChangeName = (e) => {
setName(e.target.value);
};
useEffect(() => {
// Mount & after Update Section
console.log("It is wrote before rendering with new state");
console.log(name);
return () => {
// Cleanup Part: Unmount & before Update Section
console.log("It is wrote before update with prev state");
console.log(name);
};
}, [name]);
return (
<div>
<div>
<input value={name} onChange={onChange} />
</div>
<div>์ด๋ฆ: {name}</div>
</div>
);
};
import React, { useReducer } from "react";
function reducer(state, action) {
switch (action.type) {
case "INCREMENT":
return { value: state.value + 1 };
case "DECREMENT":
return { value: state.value - 1 };
default:
return state;
}
}
const Counter = () => {
const [state, dispatch] = useReducer(reducer, { value: 0 });
};
return (
<div>
<p>ํ์ฌ ๊ฐ์ {state.value} ์
๋๋ค.</p>
<button onClick={() => dispatch({ type: "INCREMENT" })}> +1 </button>
<button onClick={() => dispatch({ type: "DECREMENT" })}> -1 </button>
</div>
);
export default Counter;
import React, {useReducer} from 'react'
function reducer(state, action) {
retun {
...state,
[action.name]: action.value
}
}
const Info = () => {
const [state, dispatch] = useReducer(reducer, {
name: "",
nickname: "",
}
const {name, nickname} = state
const onChange = e => {
dispatch(e.target)
}
return (
<div>
<div>
<input name="name" value={name} onChange={onChange} />
<input name="nickname" value={nickname} onChange={onChange} />
</div>
<div>
<div><b>์ด๋ฆ:</b> {name}</div>
<div><b>๋๋ค์:</b> {nickname}</div>
</div>
</div>
)
}
import React, { useState, useMemo } from "react";
const getAverage = (numbers) => {
console.log("ํ๊ท ๊ฐ ๊ณ์ฐ ์ค..");
if (numbers.length === 0) return 0;
const sum = numbers.reduce((a, b) => a + b);
return sum / numbers.length;
};
const Average = () => {
const [list, setList] = useState([]);
const [number, setNumber] = useState("");
const onChange = (e) => {
setNumber(e.target.value);
};
const onInsert = () => {
const nextList = list.concat(parseInt(number));
setList(nextList);
setNumber("");
};
// ์๋ avg๊ฐ ์์ด์ getAverage(list)๋ฅผ ๋ฐ๋ก ์ฐ๋ฉด ๋งค๋ฒ ์๋ก ๊ณ์ฐ
const avg = useMemo(() => getAverage(list), [list]);
return (
<div>
<input value={number} onChange={onChange} />
<button onClick={onInsert}>๋ฑ๋ก</button>
<ul>
{list.map((value, index) => (
<li key={index}>{value}</li>
))}
</ul>
<div>
<b>ํ๊ท ๊ฐ: </b> {avg}
</div>
</div>
);
};
const onChange = useCallback((e) => {
setNumber(e.target.value);
}, []);
const onInsert = useCallback(() => {
const nextList = list.concat(parseInt(number)); // ๋ถ๋ณ์ฑ!!
setList(nextList);
setNumber("");
}, [number, list]);
// ์๋์ ๋ ํจ์๋ ์ฌ์ค์ ๊ฐ์ ํจ์์ด๋ค.
useCallback(() => {
console.log("hello world");
}, []);
useMemo(() => {
const fn = () => {
console.log("hello world");
};
return fn;
}, []);
const inputEl = useRef(null)
...
const onInsert = useCallback(()=> {
...
input.El.current.focus()
})
...
return (
...
<input ref={inputEl}/>
...
)
<Router>
๊ฐ ์์ด์ผ ํจ<Link>
๋ <a>
ํ๊ทธ์ ๊ฑฐ์ ๊ฐ์ ๊ธฐ๋ฅ์ ์ํํจ<a>
๋ฅผ ์ฐ๊ฒ ๋๋ฉด ์ ์ฒด ํ์ด์ง๊ฐ refresh ๋์ด SPA๋ผ ํ ์ ์์<Link>
๋ก ๊ตฌํํด์ผํจ<Switch>
๋ url์ ๋ฐ๋ผ ํ๋์ ํ๋ฉด์ ๋ ๋๋งํจ<Switch>
์๋ ์๋ <Route>
์ค ํ๋<Switch>
๋ ํ์ฌ url๊ณผ ๋งค์น๋๋ ์ฒซ๋ฒ์งธ <Route>
๋ฅผ ๋ ๋๋งimport React from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
export default function App() {
return (
<Router>
<div>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/users">Users</Link>
</li>
</ul>
</nav>
<Switch>
<Route path="/about">
<About />
</Route>
<Route path="/users">
<Users />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</div>
</Router>
);
}
Const Action = {
TYPE: "Name of this Action"
}
dispatch(action)
์ฒ๋ผ ์ฌ์ฉconst initialState = {
counter: 1,
};
function reducer(state = initialState, action) {
switch (action.type) {
case INCREMENT:
return {
couter: state.counter + 1,
};
case DECREMENT:
return {
couter: state.counter - 1,
};
default:
return state;
}
}
import React from "react";
import "./styles.css";
import CounterContainer from "./containers/CounterContainer";
import TodosContainer from "./containers/TodosContainer";
export default function App() {
return (
<div className="App">
<CounterContainer />
<hr />
<TodosContainer />
</div>
);
}
import React from "react";
import ReactDOM from "react-dom";
import { createStore } from "redux";
import { Provider } from "react-redux";
import { composeWithDevTools } from "redux-devtools-extension";
import rootReducer from "./modules";
import App from "./App";
const store = createStore(rootReducer, composeWithDevTools());
const rootElement = document.getElementById("root");
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
rootElement
);
import React, { useCallback } from "react";
import { useSelector, useDispatch } from "react-redux";
import Counter from "../components/Counter";
import { increase, decrease } from "../modules/counter";
const CounterContainer = () => {
const number = useSelector((state) => state.counter.number);
const dispatch = useDispatch();
const onIncrease = useCallback(() => dispatch(increase()), [dispatch]);
const onDecrease = useCallback(() => dispatch(decrease()), [dispatch]);
return (
<Counter number={number} onIncrease={onIncrease} onDecrease={onDecrease} />
);
};
export default React.memo(CounterContainer);
import React from "react";
import { useSelector } from "react-redux";
import { changeInput, insert, toggle, remove } from "../modules/todos";
import Todos from "../components/Todos";
import useActions from "../lib/useActions";
const TodosContainer = () => {
const { input, todos } = useSelector(({ todos }) => ({
input: todos.input,
todos: todos.todos,
}));
const [onChangeInput, onInsert, onToggle, onRemove] = useActions(
[changeInput, insert, toggle, remove],
[]
);
return (
<Todos
input={input}
todos={todos}
onChangeInput={onChangeInput}
onInsert={onInsert}
onToggle={onToggle}
onRemove={onRemove}
/>
);
};
export default TodosContainer;
import { createAction, handleActions } from "redux-actions";
// ์ก์
ํ์
์ ์
const INCREASE = "counter/INCREASE";
const DECREASE = "counter/DECREASE";
export const increase = createAction(INCREASE);
export const decrease = createAction(DECREASE);
// ์ด๊ธฐ ์ํ ๋ฐ ๋ฆฌ๋์
const initialState = {
number: 0,
};
const counter = handleActions(
{
[INCREASE]: (state, action) => ({ number: state.number + 1 }),
[DECREASE]: (state, action) => ({ number: state.number - 1 }),
},
initialState
);
export default counter;
import { createAction, handleActions } from "redux-actions";
import produce from "immer";
const CHANGE_INPUT = "todos/CHANGE_INPUT";
const INSERT = "todos/INSERT";
const TOGGLE = "todos/TOGGLE";
const REMOVE = "todos/REMOVE";
export const changeInput = createAction(CHANGE_INPUT, (input) => input);
let id = 3;
export const insert = createAction(INSERT, (text) => ({
id: id++,
text,
done: false,
}));
export const toggle = createAction(TOGGLE, (id) => id);
export const remove = createAction(REMOVE, (id) => id);
const initialState = {
input: "",
todos: [
{
id: 1,
text: "learning basic redux",
done: true,
},
{
id: 2,
text: "using redux and react",
done: false,
},
],
};
const todos = handleActions(
{
[CHANGE_INPUT]: (state, { payload: input }) =>
produce(state, (draft) => {
draft.input = input;
}),
[INSERT]: (state, { payload: todo }) =>
produce(state, (draft) => {
draft.todos.push(todo);
}),
[TOGGLE]: (state, { payload: id }) =>
produce(state, (draft) => {
const todo = draft.todos.find((todo) => todo.id === id);
todo.done = !todo.done;
}),
[REMOVE]: (state, { payload: id }) =>
produce(state, (draft) => {
const index = draft.todos.findIndex((todo) => todo.id === id);
draft.todos.splice(index, 1);
}),
},
initialState
);
export default todos;
import { combineReducers } from "redux";
import counter from "./counter";
import todos from "./todos";
const rootReducer = combineReducers({
counter,
todos,
});
export default rootReducer;
// components/Counter.js
import React from "react";
const Counter = ({ number, onIncrease, onDecrease }) => {
return (
<div>
<h1>{number}</h1>
<div>
<button onClick={onIncrease}>+1</button>
<button onClick={onDecrease}>-1</button>
</div>
</div>
);
};
export default Counter;
import React from "react";
const TodoItem = ({ todo, onToggle, onRemove }) => {
return (
<div>
<input
type="checkbox"
onClick={() => onToggle(todo.id)}
checked={todo.done}
readOnly={true}
/>
<span style={{ textDecoration: todo.done ? "line-through" : "none" }}>
{todo.text}
</span>
<button onClick={() => onRemove(todo.id)}> delete </button>
</div>
);
};
const Todos = ({
input,
todos,
onChangeInput,
onInsert,
onToggle,
onRemove,
}) => {
const onSubmit = (e) => {
e.preventDefault();
onInsert(input);
onChangeInput("");
};
const onChange = (e) => onChangeInput(e.target.value);
return (
<div>
<form onSubmit={onSubmit}>
<input value={input} onChange={onChange} />
<button type="submit">enroll</button>
</form>
<div>
{todos.map((todo) => {
return (
<TodoItem
todo={todo}
key={todo.id}
onToggle={onToggle}
onRemove={onRemove}
/>
);
})}
</div>
</div>
);
};
export default Todos;
import { bindActionCreators } from "redux";
import { useDispatch } from "react-redux";
import { useMemo } from "react";
export default function useActions(actions, deps) {
const dispatch = useDispatch();
return useMemo(
() => {
if (Array.isArray(actions)) {
return actions.map((a) => bindActionCreators(a, dispatch));
}
return bindActionCreators(actions, dispatch);
},
deps ? [dispatch, ...deps] : [dispatch]
);
}
import React from "react";
import "./styles.css";
import CounterContainer from "./containers/CounterContainer";
import SampleContainer from "./containers/SampleContainer";
export default function App() {
return (
<div className="App">
<SampleContainer />
<CounterContainer />
</div>
);
}
import React from "react";
import ReactDOM from "react-dom";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import App from "./App";
import rootReducer from "./modules";
import { createLogger } from "redux-logger";
import ReduxThunk from "redux-thunk";
const rootElement = document.getElementById("root");
const logger = createLogger();
const store = createStore(rootReducer, applyMiddleware(logger, ReduxThunk));
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
rootElement
);
import { combineReducers } from "redux";
import counter from "./counter";
import sample from "./sample";
import loading from "./loading";
const rootReducer = combineReducers({
counter,
loading,
sample,
});
export default rootReducer;
import { createAction, handleActions } from "redux-actions";
const START_LOADING = "loading/START_LOADING";
const FINISH_LOADING = "loading/FINISH_LOADING";
export const startLoading = createAction(
START_LOADING,
(requestType) => requestType
);
export const finishLoading = createAction(
FINISH_LOADING,
(requestType) => requestType
);
const initialState = {};
const loading = handleActions(
{
[START_LOADING]: (state, action) => ({
...state,
[action.payload]: true,
}),
[FINISH_LOADING]: (state, action) => ({
...state,
[action.payload]: false,
}),
},
initialState
);
export default loading;
import { createAction, handleActions } from "redux-actions";
const INCREASE = "counter/INCREASE";
const DECREASE = "counter/DECREASE";
export const increase = createAction(INCREASE);
export const decrease = createAction(DECREASE);
export const increaseAsync = () => (dispatch) => {
setTimeout(() => {
dispatch(increase());
}, 1000);
};
export const decreaseAsync = () => (dispatch) => {
setTimeout(() => {
dispatch(decrease());
}, 1000);
};
const initialState = 0;
const counter = handleActions(
{
[INCREASE]: (state) => state + 1,
[DECREASE]: (state) => state - 1,
},
initialState
);
export default counter;
import { handleActions } from "redux-actions";
import * as api from "../lib/api";
import createRequestThunk from "../lib/createRequestThunk";
const GET_POST = "sample/GET_POST";
const GET_POST_SUCCESS = "sample/GET_POST_SUCCESS";
const GET_USERS = "sample/GET_USER";
const GET_USERS_SUCCESS = "sample/GET_USER_SUCCESS";
export const getPost = createRequestThunk(GET_POST, api.getPost);
export const getUsers = createRequestThunk(GET_USERS, api.getUsers);
const initialState = {
post: null,
users: null,
};
const sample = handleActions(
{
[GET_POST_SUCCESS]: (state, action) => ({
...state,
post: action.payload,
}),
[GET_USERS_SUCCESS]: (state, action) => ({
...state,
users: action.payload,
}),
},
initialState
);
export default sample;
import axios from "axios";
export const getPost = (id) =>
axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`);
export const getUsers = (id) =>
axios.get(`https://jsonplaceholder.typicode.com/users`);
import { startLoading, finishLoading } from "../modules/loading";
export default function createRequestThunk(type, request) {
const SUCCESS = `${type}_SUCCESS`;
const FAILURE = `${type}_FAILURE`;
return (params) => async (dispatch) => {
dispatch({ type });
dispatch(startLoading(type));
try {
const response = await request(params);
dispatch({ type: SUCCESS, payload: response.data });
dispatch(finishLoading(type));
} catch (e) {
dispatch({ type: FAILURE, payload: e, error: true });
dispatch(finishLoading(type));
throw e;
}
};
}
import React from "react";
import { connect } from "react-redux";
import { increaseAsync, decreaseAsync } from "../modules/counter";
import Counter from "../components/Counter";
const CounterContainer = ({ number, increaseAsync, decreaseAsync }) => {
return (
<Counter
number={number}
onIncrease={increaseAsync}
onDecrease={decreaseAsync}
/>
);
};
export default connect(
(state) => ({
number: state.counter,
}),
{
increaseAsync,
decreaseAsync,
}
)(CounterContainer);
import React, { useEffect } from "react";
import { connect } from "react-redux";
import Sample from "../components/Sample";
import { getPost, getUsers } from "../modules/sample";
const SampleContainer = ({
getPost,
getUsers,
post,
users,
loadingPost,
loadingUsers,
}) => {
useEffect(() => {
const fn = async () => {
try {
await getPost(1);
await getUsers(1);
} catch (e) {
console.log(e);
}
};
fn();
}, [getPost, getUsers]);
return (
<Sample
post={post}
users={users}
loadingPost={loadingPost}
loadingUsers={loadingUsers}
/>
);
};
export default connect(
({ sample, loading }) => ({
post: sample.post,
users: sample.users,
loadingPost: loading["sample/GET_POST"],
loadingUsers: loading["sample/GET_USERS"],
}),
{
getPost,
getUsers,
}
)(SampleContainer);
import React from "react";
const Counter = ({ onIncrease, onDecrease, number }) => {
return (
<div>
<h1>{number}</h1>
<button
onClick={() => {
onIncrease();
}}
>
+1
</button>
<button
onClick={() => {
onDecrease();
}}
>
-1
</button>
</div>
);
};
export default Counter;
import React from "react";
const Sample = ({ loadingPost, loadingUsers, post, users }) => {
return (
<div>
<section>
<h1>Post</h1>
{loadingPost && "loading..."}
{!loadingPost && post && (
<div>
<h3>{post.title}</h3>
<h3>{post.body}</h3>
</div>
)}
</section>
<hr />
<section>
<h1>users list</h1>
{loadingUsers && "loading..."}
{!loadingUsers && users && (
<ul>
{users.map((user) => (
<li key={user.id}>
{user.username} ({user.email})
</li>
))}
</ul>
)}
</section>
</div>
);
};
export default Sample;
import React from "react";
import "./styles.css";
import ColorBox from "./ColorBox";
import SelectColors from "./SelectColors";
import { ColorProvider } from "./colorContext";
export default function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<ColorProvider>
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
flexDirection: "column",
}}
>
<SelectColors />
<br />
<ColorBox />
</div>
</ColorProvider>
</div>
);
}
import React, { createContext, useState } from "react";
// ์ปจํ
์คํธ์ ๊ธฐ๋ณธ๊ฐ์ ์ง์
const ColorContext = createContext({
state: { color: "black", subcolor: "red" },
action: {
setColor: () => {},
setSubcolor: () => {},
},
});
export const ColorProvider = ({ children }) => {
const [color, setColor] = useState("black");
const [subcolor, setSubcolor] = useState("red");
const value = {
state: { color, subcolor },
actions: { setColor, setSubcolor },
};
return (
<ColorContext.Provider value={value}>{children}</ColorContext.Provider>
);
};
export default ColorContext;
import React, { useContext } from "react";
import ColorContext from "./colorContext";
const ColorBox = () => {
const { state } = useContext(ColorContext);
return (
<>
<div
style={{
height: "64px",
width: "64px",
background: state.color,
}}
/>
<div
style={{
height: "32px",
width: "32px",
background: state.subcolor,
}}
/>
</>
);
};
export default ColorBox;
import React, { useContext } from "react";
import ColorContext from "./colorContext";
const colors = ["red", "orange", "yellow", "green", "blue", "indigo", "violet"];
const SelectColors = () => {
const { actions } = useContext(ColorContext);
return (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
flexDirection: "column",
}}
>
<h2>Choose the colors</h2>
<div style={{ display: "flex" }}>
{colors.map((color) => (
<div
key={color}
style={{
background: color,
width: "24px",
height: "24px",
cursor: "pointer",
}}
onClick={() => {
actions.setColor(color);
}}
onContextMenu={(e) => {
e.preventDefault();
actions.setSubcolor(color);
}}
/>
))}
</div>
</div>
);
};
export default SelectColors;
import A from 'B'
๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ,import(ํ์ผ).then(result=>result.default())
๋ฅผ ์ฌ์ฉํด ๋ก๋ฉimport React, { useState, Suspense } from "react";
const SplitMe = React.lazy(() => import("./SplitMe"));
function App() {
const [visible, setVisible] = useState(false);
const onClick = () => {
setVisible(true);
};
return (
<div>
<p onClick={onClick}>Hello React!</p>
<Suspense fallback={<div>loading...</div>}>
{visible && <SplitMe />}
</Suspense>
</div>
);
}
export default React.memo(Component Name)
{[variable]: value}
<div hello>
VS {hello}
์ ์ฐจ์ดpropsName={propsValue}
๋ก ์ฐ๊ฑฐ๋ propsName
๋ง ์ ๋ ๋ฐฉ์a
์๋ d.a
๊ฐ b
์๋ d.b
๊ฐ ๋์
b
์ e.a
๊ฐ d
์ e.c
๊ฐ ๋์
console.log(`hello ${{ foo: "bar" }} ${() => "world"}`);
// ์ถ๋ ฅ ๊ฒฐ๊ณผ: "hello [object Object] () => 'world'"
console.log`hello ${{ foo: "bar" }} ${() => "world"}`;
// ์ถ๋ ฅ ๊ฒฐ๊ณผ: (3) ["hello ", " ", "", raw: Array(3)] {foo: "bar"} ()=>'world'
// ์ค๋น
import React from 'react'
const RenderPropsSample = ({children}) => {
return <div>๊ฒฐ๊ณผ: {children(5)}</div>
}
export default RenderPropsSample
// ๋์ค์ ๋ค๋ฅธ ํ์ผ์์..
<RenderPropsSample>{value => 2*value}</RenderPropsSample>
// ๊ทธ๋ฌ๋ฉด "๊ฒฐ๊ณผ: 10"๊ฐ ๋ ๋๋ง ๋จ
TAB
์ ๋๋ฅด๋ฉด,TAB
์ ๋๋ฅด๋ฉด,