TypeScriptとReact-#2応答の適用
135087 ワード
https://velog.io/@velopert/create-typescript-react-component
プロジェクトの作成
道具に入りますか? defaultProps、ProType、ContextTypeを設定すると自動的に完了しますか? 短所名の子供がゆったりした形で入っているので、道具の種類は不明です. 一部のコンポーネントにはサブコンポーネントが含まれていないため、各Propsのタイプにはサブコンポーネントが明記されている必要があります. n/a.結論
React.FCに子供道具があるのがメリットじゃないですか?
React.FCを使うと子供達が道具の中に入るそうですReactFCはなく、子供達も確認して入ってきました!
https://velog.io/@donggu/文系生-説明の-react-pprosproperties-children
A素子の間にBという素子がある場合、B素子の内容を表示するためのprops.
propsで省略できる値は
関数型構成部品を作成する場合は、矢印関数を使用します. Propsのタイプが発表されたとき タイプスクリプトとしてHooksを使用
プロジェクトの作成
https://velog.io/@swimme/react-typescript-起動 npx create-react-app ts-tutorial --template typescript
既存のCRAでは、typescriptを後に付けるだけで、タイプスクリプト設定を適用した項目を生成できます.
作成したアイテムにタイプスクリプトを適用する場合は、次のように設定します.npm install typescript @types/node @types/react @types/react-dom @types/jest
// yarn add typescript @types/node @types/react @types/react-dom @types/jest
タイプスクリプトで作成されたコンポーネント
App.tsx
import React from 'react';
import logo from './logo.svg';
import './App.css';
const App: React.FC = () => {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.tsx</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
最近の傾向は、矢印関数ではなく関数キーを使用することです.React.FC
のタイプ指定タイプを使用できますが、長い列があるそうです.
Greeting.tsx
import React from 'react';
type GreetingsProps = {
name: string;
};
const Greetings: React.FC<GreetingsProps> = ({ name }) => (
<div>Hello, {name}</div>
);
export default Greetings;
構成部品のpropsタイプを宣言する場合はtypeとinterfaceを使用できますが、1つのプロジェクト内で一貫性を保つ必要があります.
React.FCのメリットとデメリット
長所
子供たちは
npx create-react-app ts-tutorial --template typescript
npm install typescript @types/node @types/react @types/react-dom @types/jest
// yarn add typescript @types/node @types/react @types/react-dom @types/jest
App.tsx
import React from 'react';
import logo from './logo.svg';
import './App.css';
const App: React.FC = () => {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.tsx</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
最近の傾向は、矢印関数ではなく関数キーを使用することです.React.FC
のタイプ指定タイプを使用できますが、長い列があるそうです.Greeting.tsx
import React from 'react';
type GreetingsProps = {
name: string;
};
const Greetings: React.FC<GreetingsProps> = ({ name }) => (
<div>Hello, {name}</div>
);
export default Greetings;
構成部品のpropsタイプを宣言する場合はtypeとinterfaceを使用できますが、1つのプロジェクト内で一貫性を保つ必要があります.React.FCのメリットとデメリット
長所
子供たちは
React.FCに子供道具があるのがメリットじゃないですか?
React.FCを使うと子供達が道具の中に入るそうですReactFCはなく、子供達も確認して入ってきました!
子供って何?
https://velog.io/@donggu/文系生-説明の-react-pprosproperties-children
A素子の間にBという素子がある場合、B素子の内容を表示するためのprops.
// App.js
import React from 'react';
import Greeting from './Greeting'
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<Greeting text="hello">
<div>between1</div>
<div>between2</div>
</Greeting>
</header>
</div>
);
}
export default App;
// Greeting.js
import React from 'react';
type GreetingsProps = {
text: string;
};
const Greetings: React.FC<GreetingsProps> = (props) => {
console.log(props);
return (
<div>
Hello, {props.text}
{props.children}
</div>
);
};
export default Greetings;
従来の方法では、ある素子内に別の素子を配置したい場合はその素子から戻るが、propsのchildrenを使用すれば、中の素子を直接放出することができる.React.FCがないほうがいい!
// App.tsx
import React from 'react';
import Greetings from './Greeting';
const App: React.FC = () => {
return <Greetings name="Hello" />; // Property 'mark' is missing in type '{ name: string; }' but required in type 'GreetingsProps'. TS2741
};
export default App;
// Greeting.tsx
import React from 'react';
type GreetingsProps = {
name: string;
mark: string;
};
const Greeting: React.FC<GreetingsProps> = ({ name, mark }) => ( // mark = '!' 로 교체
<div>
Hello, {name} {mark}
</div>
);
Greeting.defaultProps = {
mark: '!'
};
export default Greeting;
次のコードのmark
動作を表す defaultProps
にもかかわらず mark
の価格がなければ、正常に働けないと言っています.非構造的な割り当てを行う過程では、デフォルト値を設定することで解決できますが、defaultProps
は意味がありません.import React from 'react';
type GreetingsProps = {
name: string;
mark: string;
};
const Greetings = ({ name, mark }: GreetingsProps) => (
<div>
Hello, {name} {mark}
</div>
);
Greetings.defaultProps = {
mark: '!'
};
export default Greetings;
// function 형태
function Greetings({ name, mark }: GreetingsProps) {
return (
<div>
Hello, {name} {mark}
</div>
);
}
Greetings.defaultProps = {
mark: '!'
};
export default Greetings;
React.FCを外すと、よく働きます.省略可能なprops設定
propsで省略できる値は
?
テキストを使用できます.// Greeting.tsx
import React from 'react';
type GreetingsProps = {
name: string;
mark: string;
optional?: string;
};
function Greetings({ name, mark, optional }: GreetingsProps) {
return (
<div>
Hello, {name} {mark}
{optional && <p>{optional}</p>}
</div>
);
}
Greetings.defaultProps = {
mark: '!'
};
export default Greetings;
関数を追加するpropsのタイプとしてname、mark、optionalを指定します.加えたoptionalは、値を入力する必要がないことを意味します.propsが関数を返すと
// App.tsx
import React from 'react';
import Greetings from './Greeting';
const App: React.FC = () => {
const handleClick = (text: string) => {
console.log(text)
return (text);
}
return <Greetings name="Hello" optional="asd" onClick={handleClick} />;
};
export default App;
// Greeting.tsx
import React, { useState } from 'react';
type GreetingsProps = {
name: string;
mark: string;
optional?: string;
onClick: (name: string) => string; // string인 name을 인자로 받아 string을 리턴함을 의미한다.
};
function Greetings({ name, mark, optional, onClick }: GreetingsProps) {
const [data, setData] = useState("");
const handleClick = () => {
setData(onClick(name));
}
return (
<div>
Hello, {name} {mark}
{optional && <p>{optional}</p>}
<div>
<button onClick={handleClick}>Click Me</button>
data : {data}
</div>
</div>
);
}
Greetings.defaultProps = {
mark: '!'
};
export default Greetings;
コールバック関数がpropsに移動すると、パラメータのタイプ、戻り値のタイプを指定できます.整理する
React.FC
あまりよくないです.function
キーワードを使ってもいいです.interface
または type
プロジェクト内で一貫性を保つ限り、使用できます.タイプスクリプトとしてHooksを使用
useState
// App.tsx
import React from 'react';
import Counter from './Counter';
const App: React.FC = () => {
return <Counter />;
};
export default App;
// Counter.tsx
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState<number>(0);
const onIncrease = () => setCount(count + 1);
const onDecrease = () => setCount(count - 1);
const wrongSet = () => setCount("wrong");
return (
<div>
<h1>{count}</h1>
<div>
<button onClick={onIncrease}>+1</button>
<button onClick={onDecrease}>-1</button>
<button onClick={wrongSet}>wrong</button> // Argument of type 'string' is not assignable to parameter of type 'SetStateAction<number>'.
</div>
</div>
);
}
export default Counter;
反応したものとあまり変わらない.useStateを宣言する場合は、処理する値のタイプを指定するだけです.
実際にJENERICを使わなくても、設定した初期値に基づいてタイプを類推して設定します.ステータスが空であっても、空でなくても、またはステータスタイプが複雑なオブジェクトや配列であればJENERICを使用できます.type Information = { name: string; description: string };
const [info, setInformation] = useState<Information | null>(null);
type Todo = { id: number; text: string; done: boolean };
const [todos, setTodos] = useState<Todo[]>([]); // { id: number; text: string; done: boolean } 형태의 객체를 요소로 가지는 배열의 타입이라는 뜻.
const [todos, setTodos] = useState([] as Todo[]);
配列については、上記のように空の配列だけを入れるとタイプを推定できないため、Genericsを指定するか、as
キーワードを使用する必要があります.
onChange, onSubmit
// App.tsx
import React from 'react';
import MyForm from './MyForm';
const App: React.FC = () => {
const onSubmit = (form: { name: string; description: string }) => {
console.log(form);
};
return <MyForm onSubmit={onSubmit} />;
};
export default App;
// MyForm.tsx
import React, { useState } from 'react';
type MyFormProps = {
onSubmit: (form: { name: string; description: string }) => void;
};
function MyForm({ onSubmit }: MyFormProps) {
const [form, setForm] = useState({
name: '',
description: ''
});
const { name, description } = form;
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e);
const { name, value } = e.target;
setForm({
...form,
[name]: value
});
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
onSubmit(form);
setForm({
name: '',
description: ''
}); // 초기화
};
return (
<form onSubmit={handleSubmit}>
<input name="name" value={name} onChange={onChange} />
<input name="description" value={description} onChange={onChange} />
<button type="submit">등록</button>
</form>
);
}
export default MyForm;
コードはかなり複雑で、順番に徐々に解いてみましょう.
type MyFormPropsの条件には、formというオブジェクトをパラメータとして受け入れ、何も返さないonSubmitという関数が含まれている必要があります.パラメータformは文字列nameとdescriptionキーから構成されます.
関数要素MyFormはpropsとしてonSubmitを受信し、onSubmitのタイプは設定したMyFormPropsに一致する必要があります.
onChange,onSubmitで起動したコールバック関数はeを超え,各イベントリスナーには独自のタイプがある.vscodeでイベントリスナーにマウスを置くと、どのタイプのマウスを置くべきか親切に教えてくれます.
「--jsx」フラグが指定されていない限り、jsxは使用できません。
https://chacha73.tistory.com/44
tsconfig.jsonでは、jsx:react-jsxをpreserveに変更できますが、再実行するとreact-jsxに戻ります.
vscodeの設定.jsonに"typescript.tsdk": "/Users/sham/.brew/lib/node_modules/typescript/lib"
を設定し、tsファイルの下部にあるstatus barでtypescriptバージョンを更新すればこの問題を解決できます.
useReducer
import React, { useReducer } from 'react';
type Action = { type: 'INCREASE' } | { type: 'DECREASE' }; // 사용되는 모든 액션을 나열한다.
function reducer(state: number, action: Action): number {
switch (action.type) {
case 'INCREASE':
return state + 1;
case 'DECREASE':
return state - 1;
default:
throw new Error('Unhandled action');
}
}
function Counter() {
const [count, dispatch] = useReducer(reducer, 0);
const onIncrease = () => dispatch({ type: 'INCREASE' });
const onDecrease = () => dispatch({ type: 'DECREASE' });
return (
<div>
<h1>{count}</h1>
<div>
<button onClick={onIncrease}>+1</button>
<button onClick={onDecrease}>-1</button>
</div>
</div>
);
}
export default Counter;
reduceを宣言する場合は、ステータスタイプと戻りタイプが同じである必要があります.上のコードは簡単なカウンタでstateタイプnumberを指定していますが、stateがオブジェクトタイプの場合はタイプまたはインタフェースを指定する必要があります.import React, { useReducer } from 'react';
type Color = 'red' | 'orange' | 'yellow';
type State = {
count: number;
text: string;
color: Color;
isGood: boolean;
};
type Action =
| { type: 'SET_COUNT'; count: number }
| { type: 'SET_TEXT'; text: string }
| { type: 'SET_COLOR'; color: Color }
| { type: 'TOGGLE_GOOD' };
function reducer(state: State, action: Action): State {
switch (action.type) {
case 'SET_COUNT':
return {
...state,
count: action.count // count가 자동완성되며, number 타입인걸 알 수 있습니다.
};
case 'SET_TEXT':
return {
...state,
text: action.text // text가 자동완성되며, string 타입인걸 알 수 있습니다.
};
case 'SET_COLOR':
return {
...state,
color: action.color // color 가 자동완성되며 color 가 Color 타입인걸 알 수 있습니다.
};
case 'TOGGLE_GOOD':
return {
...state,
isGood: !state.isGood
};
default:
throw new Error('Unhandled action');
}
}
function ReducerSample() {
const [state, dispatch] = useReducer(reducer, {
count: 0,
text: 'hello',
color: 'red',
isGood: true
});
const setCount = () => dispatch({ type: 'SET_COUNT', count: 5 }); // count 를 넣지 않으면 에러발생
const setText = () => dispatch({ type: 'SET_TEXT', text: 'bye' }); // text 를 넣지 않으면 에러 발생
const setColor = () => dispatch({ type: 'SET_COLOR', color: 'orange' }); // orange 를 넣지 않으면 에러 발생
const toggleGood = () => dispatch({ type: 'TOGGLE_GOOD' });
return (
<div>
<p>
<code>count: </code> {state.count}
</p>
<p>
<code>text: </code> {state.text}
</p>
<p>
<code>color: </code> {state.color}
</p>
<p>
<code>isGood: </code> {state.isGood ? 'true' : 'false'}
</p>
<div>
<button onClick={setCount}>SET_COUNT</button>
<button onClick={setText}>SET_TEXT</button>
<button onClick={setColor}>SET_COLOR</button>
<button onClick={toggleGood}>TOGGLE_GOOD</button>
</div>
</div>
);
}
export default useRef;
タイプの指定が追加されただけで、ユーザーリーダーとはあまり違いません.
useRef
応答要素が外部ライブラリのインスタンスまたはDOMを特定の値に含める場合、useRef
が使用される.refが変更されてもレンダリングされません.
変数値の管理
userefを使用する場合、実際の値はです.電流で近似して得る.ムカデでタイプを明記することができます.const id = useRef<number>(0);
const increaseId = () => {
id.current += 1;
}
import React, { useState, useRef } from 'react';
type MyFormProps = {
onSubmit: (form: { name: string; description: string }) => void;
};
function MyForm({ onSubmit }: MyFormProps) {
const inputRef = useRef<HTMLInputElement>(null);
const [form, setForm] = useState({
name: '',
description: ''
});
const { name, description } = form;
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setForm({
...form,
[name]: value
});
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
onSubmit(form);
setForm({
name: '',
description: ''
});
if (!inputRef.current) {
return;
}
inputRef.current.focus();
};
return (
<form onSubmit={handleSubmit}>
<input name="name" value={name} onChange={onChange} ref={inputRef} />
<input name="description" value={description} onChange={onChange} />
<button type="submit">등록</button>
</form>
);
}
export default MyForm;
入力した名前のinput要素を操作するために、inputRefをrefに掛けます.handleSubmitを実行する場合は、inputRefを使用してinputを操作し、入力のハイライトにします.
inputRef.current値を使用する場合はnullチェックが必要ですが、無視してレンダリングしようとするとObject is possibly 'null'. TS2531
というエラーが発生します.import React from 'react';
import MyForm from './MyForm';
const App: React.FC = () => {
const onSubmit = (form: { name: string; description: string }) => {
console.log(form);
};
return <MyForm onSubmit={onSubmit} />;
};
export default App;
タイプスクリプトとしてContext APIを使用
ステータスのみのContextとスケジューリング用のContextを2つ作成します.
ステータス、dispatchを1つのContextで管理することはできないと思いますが、ステータス値は必要なく、dispatchだけが必要な構成部品もステータス更新時に一緒にレンダリングされます.無駄なレンダリングを防ぐことができます.
ステータスContext、派遣Contextの設定
import { createContext, Dispatch } from 'react';
export type Todo = {
id: number;
text: string;
done: boolean;
};
type TodosState = Todo[];
const TodosStateContext = createContext<TodosState | undefined>(undefined);
type Action =
| { type: 'CREATE'; text: string }
| { type: 'TOGGLE'; id: number }
| { type: 'REMOVE'; id: number };
type TodosDispatch = Dispatch<Action>;
const TodosDispatchContext = createContext<TodosDispatch | undefined>(
undefined
);
モジュールのように書き込むために、個別のファイルを作成して管理します.上のTodosStateContextは、Todoを配列要素とするTodosStateタイプに従います.TodosDispatchContextsには、Actionと同じ形式のdispatchが含まれています.動作に必要な値が存在しない場合は、エラーが放出されます.
Providerを使用しない場合、Context値は未定義である必要があります.タイプに明記してください.
Reduser設定
import { createContext, Dispatch } from 'react';
export type Todo = {
id: number;
text: string;
done: boolean;
};
type TodosState = Todo[];
const TodosStateContext = createContext<TodosState | undefined>(undefined);
type Action =
| { type: 'CREATE'; text: string }
| { type: 'TOGGLE'; id: number }
| { type: 'REMOVE'; id: number };
type TodosDispatch = Dispatch<Action>;
const TodosDispatchContext = createContext<TodosDispatch | undefined>(
undefined
);
function todosReducer(state: TodosState, action: Action): TodosState {
switch (action.type) {
case 'CREATE':
const nextId = Math.max(...state.map(todo => todo.id)) + 1;
return state.concat({
id: nextId,
text: action.text,
done: false
});
case 'TOGGLE':
return state.map(todo =>
todo.id === action.id ? { ...todo, done: !todo.done } : todo
);
case 'REMOVE':
return state.filter(todo => todo.id !== action.id);
default:
throw new Error('Unhandled action');
}
}
プロバイダ設定
import React, { createContext, Dispatch, useReducer } from 'react';
(...) // (이전 코드 생략)
export function TodosContextProvider({ children }: { children: React.ReactNode }) {
const [todos, dispatch] = useReducer(todosReducer, [
{
id: 1,
text: 'Context API 배우기',
done: true
},
{
id: 2,
text: 'TypeScript 배우기',
done: true
},
{
id: 3,
text: 'TypeScript 와 Context API 함께 사용하기',
done: false
}
]);
return (
<TodosDispatchContext.Provider value={dispatch}>
<TodosStateContext.Provider value={todos}>
{children}
</TodosStateContext.Provider>
</TodosDispatchContext.Provider>
);
}
カスタムホームページの作成
import React, { createContext, Dispatch, useReducer, useContext } from 'react';
export function useTodosState() {
const state = useContext(TodosStateContext);
if (!state) throw new Error('TodosProvider not found');
return state;
}
export function useTodosDispatch() {
const dispatch = useContext(TodosDispatchContext);
if (!dispatch) throw new Error('TodosProvider not found');
return dispatch;
}
useContext
を使用してContextの値を使用する場合は、この値が有効かどうかを確認する必要があります.Custom Hookを使用して互換性チェックを自動的に行うことができます.
構成部品でのContextの使用
アプリケーションコンポーネントは、既存のコンテンツを保護するためにTodosContextProviderを呼び出す必要があります.// src/App.tsx
import React from 'react';
import TodoForm from './components/TodoForm';
import TodoList from './components/TodoList';
import { TodosContextProvider } from './contexts/TodosContext';
const App = () => {
return (
<TodosContextProvider>
<TodoForm />
<TodoList />
</TodosContextProvider>
);
};
export default App;
Contextの検索(userTodoState)
// src/components/TodoList.tsx
import React from 'react';
import TodoItem from './TodoItem';
import { useTodosState } from '../contexts/TodosContext';
function TodoList() {
const todos = useTodosState();
return (
<ul>
{todos.map(todo => (
<TodoItem todo={todo} key={todo.id} />
))}
</ul>
);
}
export default TodoList;
Dispatchの使用-登録
// src/components/TodoForm.tsx
import React, { useState } from 'react';
import { useTodosDispatch } from '../contexts/TodosContext';
function TodoForm() {
const [value, setValue] = useState('');
const dispatch = useTodosDispatch();
const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
dispatch({
type: 'CREATE',
text: value
});
setValue('');
};
return (
<form onSubmit={onSubmit}>
<input
value={value}
placeholder="무엇을 하실 건가요?"
onChange={e => setValue(e.target.value)}
/>
<button>등록</button>
</form>
);
}
export default TodoForm;
Dispatch-切り替え、削除
import React from 'react';
import './TodoItem.css';
import { useTodosDispatch, Todo } from '../contexts/TodosContext';
type TodoItemProps = {
todo: Todo; // TodoContext 에서 선언했던 타입을 불러왔습니다.
};
function TodoItem({ todo }: TodoItemProps) {
const dispatch = useTodosDispatch();
const onToggle = () => {
dispatch({
type: 'TOGGLE',
id: todo.id
});
};
const onRemove = () => {
dispatch({
type: 'REMOVE',
id: todo.id
});
};
return (
<li className={`TodoItem ${todo.done ? 'done' : ''}`}>
<span className="text" onClick={onToggle}>
{todo.text}
</span>
<span className="remove" onClick={onRemove}>
(X)
</span>
</li>
);
}
export default TodoItem;
Reference
この問題について(TypeScriptとReact-#2応答の適用), 我々は、より多くの情報をここで見つけました
https://velog.io/@sham/TypeScript와-React-2-리액트-적용
テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol
// App.tsx
import React from 'react';
import Counter from './Counter';
const App: React.FC = () => {
return <Counter />;
};
export default App;
// Counter.tsx
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState<number>(0);
const onIncrease = () => setCount(count + 1);
const onDecrease = () => setCount(count - 1);
const wrongSet = () => setCount("wrong");
return (
<div>
<h1>{count}</h1>
<div>
<button onClick={onIncrease}>+1</button>
<button onClick={onDecrease}>-1</button>
<button onClick={wrongSet}>wrong</button> // Argument of type 'string' is not assignable to parameter of type 'SetStateAction<number>'.
</div>
</div>
);
}
export default Counter;
type Information = { name: string; description: string };
const [info, setInformation] = useState<Information | null>(null);
type Todo = { id: number; text: string; done: boolean };
const [todos, setTodos] = useState<Todo[]>([]); // { id: number; text: string; done: boolean } 형태의 객체를 요소로 가지는 배열의 타입이라는 뜻.
const [todos, setTodos] = useState([] as Todo[]);
// App.tsx
import React from 'react';
import MyForm from './MyForm';
const App: React.FC = () => {
const onSubmit = (form: { name: string; description: string }) => {
console.log(form);
};
return <MyForm onSubmit={onSubmit} />;
};
export default App;
// MyForm.tsx
import React, { useState } from 'react';
type MyFormProps = {
onSubmit: (form: { name: string; description: string }) => void;
};
function MyForm({ onSubmit }: MyFormProps) {
const [form, setForm] = useState({
name: '',
description: ''
});
const { name, description } = form;
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e);
const { name, value } = e.target;
setForm({
...form,
[name]: value
});
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
onSubmit(form);
setForm({
name: '',
description: ''
}); // 초기화
};
return (
<form onSubmit={handleSubmit}>
<input name="name" value={name} onChange={onChange} />
<input name="description" value={description} onChange={onChange} />
<button type="submit">등록</button>
</form>
);
}
export default MyForm;
import React, { useReducer } from 'react';
type Action = { type: 'INCREASE' } | { type: 'DECREASE' }; // 사용되는 모든 액션을 나열한다.
function reducer(state: number, action: Action): number {
switch (action.type) {
case 'INCREASE':
return state + 1;
case 'DECREASE':
return state - 1;
default:
throw new Error('Unhandled action');
}
}
function Counter() {
const [count, dispatch] = useReducer(reducer, 0);
const onIncrease = () => dispatch({ type: 'INCREASE' });
const onDecrease = () => dispatch({ type: 'DECREASE' });
return (
<div>
<h1>{count}</h1>
<div>
<button onClick={onIncrease}>+1</button>
<button onClick={onDecrease}>-1</button>
</div>
</div>
);
}
export default Counter;
import React, { useReducer } from 'react';
type Color = 'red' | 'orange' | 'yellow';
type State = {
count: number;
text: string;
color: Color;
isGood: boolean;
};
type Action =
| { type: 'SET_COUNT'; count: number }
| { type: 'SET_TEXT'; text: string }
| { type: 'SET_COLOR'; color: Color }
| { type: 'TOGGLE_GOOD' };
function reducer(state: State, action: Action): State {
switch (action.type) {
case 'SET_COUNT':
return {
...state,
count: action.count // count가 자동완성되며, number 타입인걸 알 수 있습니다.
};
case 'SET_TEXT':
return {
...state,
text: action.text // text가 자동완성되며, string 타입인걸 알 수 있습니다.
};
case 'SET_COLOR':
return {
...state,
color: action.color // color 가 자동완성되며 color 가 Color 타입인걸 알 수 있습니다.
};
case 'TOGGLE_GOOD':
return {
...state,
isGood: !state.isGood
};
default:
throw new Error('Unhandled action');
}
}
function ReducerSample() {
const [state, dispatch] = useReducer(reducer, {
count: 0,
text: 'hello',
color: 'red',
isGood: true
});
const setCount = () => dispatch({ type: 'SET_COUNT', count: 5 }); // count 를 넣지 않으면 에러발생
const setText = () => dispatch({ type: 'SET_TEXT', text: 'bye' }); // text 를 넣지 않으면 에러 발생
const setColor = () => dispatch({ type: 'SET_COLOR', color: 'orange' }); // orange 를 넣지 않으면 에러 발생
const toggleGood = () => dispatch({ type: 'TOGGLE_GOOD' });
return (
<div>
<p>
<code>count: </code> {state.count}
</p>
<p>
<code>text: </code> {state.text}
</p>
<p>
<code>color: </code> {state.color}
</p>
<p>
<code>isGood: </code> {state.isGood ? 'true' : 'false'}
</p>
<div>
<button onClick={setCount}>SET_COUNT</button>
<button onClick={setText}>SET_TEXT</button>
<button onClick={setColor}>SET_COLOR</button>
<button onClick={toggleGood}>TOGGLE_GOOD</button>
</div>
</div>
);
}
export default useRef;
応答要素が外部ライブラリのインスタンスまたはDOMを特定の値に含める場合、
useRef
が使用される.refが変更されてもレンダリングされません.変数値の管理
userefを使用する場合、実際の値はです.電流で近似して得る.ムカデでタイプを明記することができます.
const id = useRef<number>(0);
const increaseId = () => {
id.current += 1;
}
import React, { useState, useRef } from 'react';
type MyFormProps = {
onSubmit: (form: { name: string; description: string }) => void;
};
function MyForm({ onSubmit }: MyFormProps) {
const inputRef = useRef<HTMLInputElement>(null);
const [form, setForm] = useState({
name: '',
description: ''
});
const { name, description } = form;
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setForm({
...form,
[name]: value
});
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
onSubmit(form);
setForm({
name: '',
description: ''
});
if (!inputRef.current) {
return;
}
inputRef.current.focus();
};
return (
<form onSubmit={handleSubmit}>
<input name="name" value={name} onChange={onChange} ref={inputRef} />
<input name="description" value={description} onChange={onChange} />
<button type="submit">등록</button>
</form>
);
}
export default MyForm;
入力した名前のinput要素を操作するために、inputRefをrefに掛けます.handleSubmitを実行する場合は、inputRefを使用してinputを操作し、入力のハイライトにします.inputRef.current値を使用する場合はnullチェックが必要ですが、無視してレンダリングしようとすると
Object is possibly 'null'. TS2531
というエラーが発生します.import React from 'react';
import MyForm from './MyForm';
const App: React.FC = () => {
const onSubmit = (form: { name: string; description: string }) => {
console.log(form);
};
return <MyForm onSubmit={onSubmit} />;
};
export default App;
タイプスクリプトとしてContext APIを使用
ステータスのみのContextとスケジューリング用のContextを2つ作成します.
ステータス、dispatchを1つのContextで管理することはできないと思いますが、ステータス値は必要なく、dispatchだけが必要な構成部品もステータス更新時に一緒にレンダリングされます.無駄なレンダリングを防ぐことができます.
ステータスContext、派遣Contextの設定
import { createContext, Dispatch } from 'react';
export type Todo = {
id: number;
text: string;
done: boolean;
};
type TodosState = Todo[];
const TodosStateContext = createContext<TodosState | undefined>(undefined);
type Action =
| { type: 'CREATE'; text: string }
| { type: 'TOGGLE'; id: number }
| { type: 'REMOVE'; id: number };
type TodosDispatch = Dispatch<Action>;
const TodosDispatchContext = createContext<TodosDispatch | undefined>(
undefined
);
モジュールのように書き込むために、個別のファイルを作成して管理します.上のTodosStateContextは、Todoを配列要素とするTodosStateタイプに従います.TodosDispatchContextsには、Actionと同じ形式のdispatchが含まれています.動作に必要な値が存在しない場合は、エラーが放出されます.
Providerを使用しない場合、Context値は未定義である必要があります.タイプに明記してください.
Reduser設定
import { createContext, Dispatch } from 'react';
export type Todo = {
id: number;
text: string;
done: boolean;
};
type TodosState = Todo[];
const TodosStateContext = createContext<TodosState | undefined>(undefined);
type Action =
| { type: 'CREATE'; text: string }
| { type: 'TOGGLE'; id: number }
| { type: 'REMOVE'; id: number };
type TodosDispatch = Dispatch<Action>;
const TodosDispatchContext = createContext<TodosDispatch | undefined>(
undefined
);
function todosReducer(state: TodosState, action: Action): TodosState {
switch (action.type) {
case 'CREATE':
const nextId = Math.max(...state.map(todo => todo.id)) + 1;
return state.concat({
id: nextId,
text: action.text,
done: false
});
case 'TOGGLE':
return state.map(todo =>
todo.id === action.id ? { ...todo, done: !todo.done } : todo
);
case 'REMOVE':
return state.filter(todo => todo.id !== action.id);
default:
throw new Error('Unhandled action');
}
}
プロバイダ設定
import React, { createContext, Dispatch, useReducer } from 'react';
(...) // (이전 코드 생략)
export function TodosContextProvider({ children }: { children: React.ReactNode }) {
const [todos, dispatch] = useReducer(todosReducer, [
{
id: 1,
text: 'Context API 배우기',
done: true
},
{
id: 2,
text: 'TypeScript 배우기',
done: true
},
{
id: 3,
text: 'TypeScript 와 Context API 함께 사용하기',
done: false
}
]);
return (
<TodosDispatchContext.Provider value={dispatch}>
<TodosStateContext.Provider value={todos}>
{children}
</TodosStateContext.Provider>
</TodosDispatchContext.Provider>
);
}
カスタムホームページの作成
import React, { createContext, Dispatch, useReducer, useContext } from 'react';
export function useTodosState() {
const state = useContext(TodosStateContext);
if (!state) throw new Error('TodosProvider not found');
return state;
}
export function useTodosDispatch() {
const dispatch = useContext(TodosDispatchContext);
if (!dispatch) throw new Error('TodosProvider not found');
return dispatch;
}
useContext
を使用してContextの値を使用する場合は、この値が有効かどうかを確認する必要があります.Custom Hookを使用して互換性チェックを自動的に行うことができます.
構成部品でのContextの使用
アプリケーションコンポーネントは、既存のコンテンツを保護するためにTodosContextProviderを呼び出す必要があります.// src/App.tsx
import React from 'react';
import TodoForm from './components/TodoForm';
import TodoList from './components/TodoList';
import { TodosContextProvider } from './contexts/TodosContext';
const App = () => {
return (
<TodosContextProvider>
<TodoForm />
<TodoList />
</TodosContextProvider>
);
};
export default App;
Contextの検索(userTodoState)
// src/components/TodoList.tsx
import React from 'react';
import TodoItem from './TodoItem';
import { useTodosState } from '../contexts/TodosContext';
function TodoList() {
const todos = useTodosState();
return (
<ul>
{todos.map(todo => (
<TodoItem todo={todo} key={todo.id} />
))}
</ul>
);
}
export default TodoList;
Dispatchの使用-登録
// src/components/TodoForm.tsx
import React, { useState } from 'react';
import { useTodosDispatch } from '../contexts/TodosContext';
function TodoForm() {
const [value, setValue] = useState('');
const dispatch = useTodosDispatch();
const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
dispatch({
type: 'CREATE',
text: value
});
setValue('');
};
return (
<form onSubmit={onSubmit}>
<input
value={value}
placeholder="무엇을 하실 건가요?"
onChange={e => setValue(e.target.value)}
/>
<button>등록</button>
</form>
);
}
export default TodoForm;
Dispatch-切り替え、削除
import React from 'react';
import './TodoItem.css';
import { useTodosDispatch, Todo } from '../contexts/TodosContext';
type TodoItemProps = {
todo: Todo; // TodoContext 에서 선언했던 타입을 불러왔습니다.
};
function TodoItem({ todo }: TodoItemProps) {
const dispatch = useTodosDispatch();
const onToggle = () => {
dispatch({
type: 'TOGGLE',
id: todo.id
});
};
const onRemove = () => {
dispatch({
type: 'REMOVE',
id: todo.id
});
};
return (
<li className={`TodoItem ${todo.done ? 'done' : ''}`}>
<span className="text" onClick={onToggle}>
{todo.text}
</span>
<span className="remove" onClick={onRemove}>
(X)
</span>
</li>
);
}
export default TodoItem;
Reference
この問題について(TypeScriptとReact-#2応答の適用), 我々は、より多くの情報をここで見つけました
https://velog.io/@sham/TypeScript와-React-2-리액트-적용
テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol
import { createContext, Dispatch } from 'react';
export type Todo = {
id: number;
text: string;
done: boolean;
};
type TodosState = Todo[];
const TodosStateContext = createContext<TodosState | undefined>(undefined);
type Action =
| { type: 'CREATE'; text: string }
| { type: 'TOGGLE'; id: number }
| { type: 'REMOVE'; id: number };
type TodosDispatch = Dispatch<Action>;
const TodosDispatchContext = createContext<TodosDispatch | undefined>(
undefined
);
import { createContext, Dispatch } from 'react';
export type Todo = {
id: number;
text: string;
done: boolean;
};
type TodosState = Todo[];
const TodosStateContext = createContext<TodosState | undefined>(undefined);
type Action =
| { type: 'CREATE'; text: string }
| { type: 'TOGGLE'; id: number }
| { type: 'REMOVE'; id: number };
type TodosDispatch = Dispatch<Action>;
const TodosDispatchContext = createContext<TodosDispatch | undefined>(
undefined
);
function todosReducer(state: TodosState, action: Action): TodosState {
switch (action.type) {
case 'CREATE':
const nextId = Math.max(...state.map(todo => todo.id)) + 1;
return state.concat({
id: nextId,
text: action.text,
done: false
});
case 'TOGGLE':
return state.map(todo =>
todo.id === action.id ? { ...todo, done: !todo.done } : todo
);
case 'REMOVE':
return state.filter(todo => todo.id !== action.id);
default:
throw new Error('Unhandled action');
}
}
import React, { createContext, Dispatch, useReducer } from 'react';
(...) // (이전 코드 생략)
export function TodosContextProvider({ children }: { children: React.ReactNode }) {
const [todos, dispatch] = useReducer(todosReducer, [
{
id: 1,
text: 'Context API 배우기',
done: true
},
{
id: 2,
text: 'TypeScript 배우기',
done: true
},
{
id: 3,
text: 'TypeScript 와 Context API 함께 사용하기',
done: false
}
]);
return (
<TodosDispatchContext.Provider value={dispatch}>
<TodosStateContext.Provider value={todos}>
{children}
</TodosStateContext.Provider>
</TodosDispatchContext.Provider>
);
}
import React, { createContext, Dispatch, useReducer, useContext } from 'react';
export function useTodosState() {
const state = useContext(TodosStateContext);
if (!state) throw new Error('TodosProvider not found');
return state;
}
export function useTodosDispatch() {
const dispatch = useContext(TodosDispatchContext);
if (!dispatch) throw new Error('TodosProvider not found');
return dispatch;
}
// src/App.tsx
import React from 'react';
import TodoForm from './components/TodoForm';
import TodoList from './components/TodoList';
import { TodosContextProvider } from './contexts/TodosContext';
const App = () => {
return (
<TodosContextProvider>
<TodoForm />
<TodoList />
</TodosContextProvider>
);
};
export default App;
// src/components/TodoList.tsx
import React from 'react';
import TodoItem from './TodoItem';
import { useTodosState } from '../contexts/TodosContext';
function TodoList() {
const todos = useTodosState();
return (
<ul>
{todos.map(todo => (
<TodoItem todo={todo} key={todo.id} />
))}
</ul>
);
}
export default TodoList;
// src/components/TodoForm.tsx
import React, { useState } from 'react';
import { useTodosDispatch } from '../contexts/TodosContext';
function TodoForm() {
const [value, setValue] = useState('');
const dispatch = useTodosDispatch();
const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
dispatch({
type: 'CREATE',
text: value
});
setValue('');
};
return (
<form onSubmit={onSubmit}>
<input
value={value}
placeholder="무엇을 하실 건가요?"
onChange={e => setValue(e.target.value)}
/>
<button>등록</button>
</form>
);
}
export default TodoForm;
import React from 'react';
import './TodoItem.css';
import { useTodosDispatch, Todo } from '../contexts/TodosContext';
type TodoItemProps = {
todo: Todo; // TodoContext 에서 선언했던 타입을 불러왔습니다.
};
function TodoItem({ todo }: TodoItemProps) {
const dispatch = useTodosDispatch();
const onToggle = () => {
dispatch({
type: 'TOGGLE',
id: todo.id
});
};
const onRemove = () => {
dispatch({
type: 'REMOVE',
id: todo.id
});
};
return (
<li className={`TodoItem ${todo.done ? 'done' : ''}`}>
<span className="text" onClick={onToggle}>
{todo.text}
</span>
<span className="remove" onClick={onRemove}>
(X)
</span>
</li>
);
}
export default TodoItem;
Reference
この問題について(TypeScriptとReact-#2応答の適用), 我々は、より多くの情報をここで見つけました https://velog.io/@sham/TypeScript와-React-2-리액트-적용テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol