[251120] test: hooks - usePrevious.js, usePrevious.test.js, usePreviou...
🕐 커밋 시간: 2025. 11. 20. 06:05:41 📊 변경 통계: • 총 파일: 3개 • 추가: +20줄 • 삭제: -7줄 📁 추가된 파일: + com.twin.app.shoptime/src/hooks/usePrevious.test.js + com.twin.app.shoptime/src/hooks/usePreviousExample.jsx 📝 수정된 파일: ~ com.twin.app.shoptime/src/hooks/usePrevious.js 🔧 함수 변경 내용: 📄 com.twin.app.shoptime/src/hooks/usePrevious.js (javascript): ❌ Deleted: usePrevious() 📄 com.twin.app.shoptime/src/hooks/usePreviousExample.jsx (javascript): ✅ Added: handleUpdate() 🔧 주요 변경 내용: • 핵심 비즈니스 로직 개선 • 테스트 커버리지 및 안정성 향상
This commit is contained in:
@@ -1,13 +1,26 @@
|
||||
import {useRef, useEffect} from 'react';
|
||||
import { useRef, useEffect } from 'react';
|
||||
|
||||
function usePrevious (value) {
|
||||
const ref = useRef();
|
||||
/**
|
||||
* usePrevious - React 16.7 전용
|
||||
* @param {*} value – 현재값 (배열, 객체, 프롭 등)
|
||||
* @param {*} [initial] – 최초 렌더시 반환할 값 (선택사항, default: undefined)
|
||||
* @return {*} – 이전 렌더에서의 값
|
||||
*
|
||||
* 사용 예시
|
||||
* const prev = usePrevious(value);
|
||||
* if (prev !== value) { … }
|
||||
*
|
||||
* // 초기값 지정
|
||||
* const prev = usePrevious(count, 0);
|
||||
*/
|
||||
export default function usePrevious(value, initial) {
|
||||
// useRef 가 저장한 객체는 렌더 사이에서도 동일합니다.
|
||||
const ref = useRef(initial);
|
||||
|
||||
useEffect(() => {
|
||||
ref.current = value;
|
||||
}, [value]);
|
||||
// value 가 바뀔 때마다 ref 를 갱신
|
||||
useEffect(() => {
|
||||
ref.current = value;
|
||||
}, [value]); // value 의 변경만 감지
|
||||
|
||||
return ref;
|
||||
return ref.current;
|
||||
}
|
||||
|
||||
export default usePrevious;
|
||||
|
||||
309
com.twin.app.shoptime/src/hooks/usePrevious.test.js
Normal file
309
com.twin.app.shoptime/src/hooks/usePrevious.test.js
Normal file
@@ -0,0 +1,309 @@
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import usePrevious from './usePrevious';
|
||||
|
||||
describe('usePrevious', () => {
|
||||
// 단일 값 테스트
|
||||
describe('단일 값 추적', () => {
|
||||
it('초기값 없이 호출하면 초기 렌더링에서 undefined를 반환해야 한다', () => {
|
||||
const { result } = renderHook(() => usePrevious(0));
|
||||
expect(result.current).toBeUndefined();
|
||||
});
|
||||
|
||||
it('초기값이 지정되면 초기 렌더링에서 초기값을 반환해야 한다', () => {
|
||||
const { result } = renderHook(() => usePrevious(0, -1));
|
||||
expect(result.current).toBe(-1);
|
||||
});
|
||||
|
||||
it('값이 변경되면 이전 값을 반환해야 한다', () => {
|
||||
const { result, rerender } = renderHook(({ value }) => usePrevious(value), {
|
||||
initialProps: { value: 0 },
|
||||
});
|
||||
|
||||
expect(result.current).toBeUndefined();
|
||||
|
||||
rerender({ value: 1 });
|
||||
expect(result.current).toBe(0);
|
||||
|
||||
rerender({ value: 2 });
|
||||
expect(result.current).toBe(1);
|
||||
|
||||
rerender({ value: 3 });
|
||||
expect(result.current).toBe(2);
|
||||
});
|
||||
|
||||
it('숫자 값을 정확히 추적해야 한다', () => {
|
||||
const { result, rerender } = renderHook(({ num }) => usePrevious(num), {
|
||||
initialProps: { num: 100 },
|
||||
});
|
||||
|
||||
rerender({ num: 200 });
|
||||
expect(result.current).toBe(100);
|
||||
|
||||
rerender({ num: 300 });
|
||||
expect(result.current).toBe(200);
|
||||
});
|
||||
|
||||
it('문자열 값을 정확히 추적해야 한다', () => {
|
||||
const { result, rerender } = renderHook(({ str }) => usePrevious(str), {
|
||||
initialProps: { str: 'hello' },
|
||||
});
|
||||
|
||||
rerender({ str: 'world' });
|
||||
expect(result.current).toBe('hello');
|
||||
|
||||
rerender({ str: 'react' });
|
||||
expect(result.current).toBe('world');
|
||||
});
|
||||
|
||||
it('boolean 값을 정확히 추적해야 한다', () => {
|
||||
const { result, rerender } = renderHook(({ bool }) => usePrevious(bool), {
|
||||
initialProps: { bool: true },
|
||||
});
|
||||
|
||||
rerender({ bool: false });
|
||||
expect(result.current).toBe(true);
|
||||
|
||||
rerender({ bool: true });
|
||||
expect(result.current).toBe(false);
|
||||
});
|
||||
|
||||
it('null 값을 추적할 수 있어야 한다', () => {
|
||||
const { result, rerender } = renderHook(({ val }) => usePrevious(val), {
|
||||
initialProps: { val: null },
|
||||
});
|
||||
|
||||
rerender({ val: 'something' });
|
||||
expect(result.current).toBeNull();
|
||||
|
||||
rerender({ val: null });
|
||||
expect(result.current).toBe('something');
|
||||
});
|
||||
});
|
||||
|
||||
// 객체 테스트
|
||||
describe('객체 값 추적', () => {
|
||||
it('객체를 추적하고 이전 객체를 반환해야 한다', () => {
|
||||
const obj1 = { name: 'John', age: 30 };
|
||||
const obj2 = { name: 'Jane', age: 25 };
|
||||
|
||||
const { result, rerender } = renderHook(({ obj }) => usePrevious(obj), {
|
||||
initialProps: { obj: obj1 },
|
||||
});
|
||||
|
||||
expect(result.current).toBeUndefined();
|
||||
|
||||
rerender({ obj: obj2 });
|
||||
expect(result.current).toBe(obj1);
|
||||
expect(result.current.name).toBe('John');
|
||||
expect(result.current.age).toBe(30);
|
||||
});
|
||||
|
||||
it('중첩된 객체를 추적할 수 있어야 한다', () => {
|
||||
const obj1 = { user: { name: 'John', profile: { age: 30 } } };
|
||||
const obj2 = { user: { name: 'Jane', profile: { age: 25 } } };
|
||||
|
||||
const { result, rerender } = renderHook(({ obj }) => usePrevious(obj), {
|
||||
initialProps: { obj: obj1 },
|
||||
});
|
||||
|
||||
rerender({ obj: obj2 });
|
||||
expect(result.current.user.name).toBe('John');
|
||||
expect(result.current.user.profile.age).toBe(30);
|
||||
});
|
||||
|
||||
it('변동 감지에 사용할 수 있어야 한다', () => {
|
||||
const obj1 = { name: 'John', age: 30 };
|
||||
const obj2 = { name: 'Jane', age: 30 };
|
||||
|
||||
const { result, rerender } = renderHook(
|
||||
({ obj }) => {
|
||||
const prev = usePrevious(obj);
|
||||
return {
|
||||
prev,
|
||||
nameChanged: prev?.name !== obj.name,
|
||||
ageChanged: prev?.age !== obj.age,
|
||||
};
|
||||
},
|
||||
{ initialProps: { obj: obj1 } }
|
||||
);
|
||||
|
||||
expect(result.current.nameChanged).toBeUndefined();
|
||||
|
||||
rerender({ obj: obj2 });
|
||||
expect(result.current.nameChanged).toBe(true);
|
||||
expect(result.current.ageChanged).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
// 배열 테스트
|
||||
describe('배열 값 추적', () => {
|
||||
it('배열을 추적하고 이전 배열을 반환해야 한다', () => {
|
||||
const arr1 = [1, 2, 3];
|
||||
const arr2 = [4, 5, 6];
|
||||
|
||||
const { result, rerender } = renderHook(({ arr }) => usePrevious(arr), {
|
||||
initialProps: { arr: arr1 },
|
||||
});
|
||||
|
||||
expect(result.current).toBeUndefined();
|
||||
|
||||
rerender({ arr: arr2 });
|
||||
expect(result.current).toEqual([1, 2, 3]);
|
||||
expect(result.current).not.toBe(arr2);
|
||||
});
|
||||
|
||||
it('배열 요소의 이전 값을 추적할 수 있어야 한다', () => {
|
||||
const { result, rerender } = renderHook(
|
||||
({ a, b }) => {
|
||||
const [prevA, prevB] = usePrevious([a, b]) || [];
|
||||
return { prevA, prevB };
|
||||
},
|
||||
{ initialProps: { a: 1, b: 2 } }
|
||||
);
|
||||
|
||||
expect(result.current.prevA).toBeUndefined();
|
||||
|
||||
rerender({ a: 10, b: 20 });
|
||||
expect(result.current.prevA).toBe(1);
|
||||
expect(result.current.prevB).toBe(2);
|
||||
|
||||
rerender({ a: 100, b: 200 });
|
||||
expect(result.current.prevA).toBe(10);
|
||||
expect(result.current.prevB).toBe(20);
|
||||
});
|
||||
|
||||
it('복잡한 배열 요소를 추적할 수 있어야 한다', () => {
|
||||
const arr1 = [{ id: 1 }, { id: 2 }];
|
||||
const arr2 = [{ id: 3 }, { id: 4 }];
|
||||
|
||||
const { result, rerender } = renderHook(({ arr }) => usePrevious(arr), {
|
||||
initialProps: { arr: arr1 },
|
||||
});
|
||||
|
||||
rerender({ arr: arr2 });
|
||||
expect(result.current[0].id).toBe(1);
|
||||
expect(result.current[1].id).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
// 변동 감지 테스트
|
||||
describe('변동 감지 활용', () => {
|
||||
it('값이 변경되었는지 감지할 수 있어야 한다', () => {
|
||||
const { result, rerender } = renderHook(
|
||||
({ value }) => {
|
||||
const prev = usePrevious(value);
|
||||
return prev !== value;
|
||||
},
|
||||
{ initialProps: { value: 'initial' } }
|
||||
);
|
||||
|
||||
expect(result.current).toBe(false);
|
||||
|
||||
rerender({ value: 'changed' });
|
||||
expect(result.current).toBe(true);
|
||||
|
||||
rerender({ value: 'changed' });
|
||||
expect(result.current).toBe(false);
|
||||
});
|
||||
|
||||
it('깊은 비교 없이도 객체 필드 변경을 감지할 수 있어야 한다', () => {
|
||||
const { result, rerender } = renderHook(
|
||||
({ obj }) => {
|
||||
const prev = usePrevious(obj);
|
||||
return {
|
||||
prevValue: prev?.value,
|
||||
changed: prev?.value !== obj.value,
|
||||
};
|
||||
},
|
||||
{ initialProps: { obj: { value: 100 } } }
|
||||
);
|
||||
|
||||
rerender({ obj: { value: 200 } });
|
||||
expect(result.current.prevValue).toBe(100);
|
||||
expect(result.current.changed).toBe(true);
|
||||
});
|
||||
|
||||
it('여러 필드 변경을 각각 추적할 수 있어야 한다', () => {
|
||||
const { result, rerender } = renderHook(
|
||||
({ name, age, email }) => {
|
||||
const prev = usePrevious({ name, age, email });
|
||||
return {
|
||||
nameChanged: prev?.name !== name,
|
||||
ageChanged: prev?.age !== age,
|
||||
emailChanged: prev?.email !== email,
|
||||
};
|
||||
},
|
||||
{ initialProps: { name: 'John', age: 30, email: 'john@example.com' } }
|
||||
);
|
||||
|
||||
rerender({ name: 'Jane', age: 30, email: 'john@example.com' });
|
||||
expect(result.current.nameChanged).toBe(true);
|
||||
expect(result.current.ageChanged).toBe(false);
|
||||
expect(result.current.emailChanged).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
// 엣지 케이스
|
||||
describe('엣지 케이스', () => {
|
||||
it('동일한 값으로 리렌더링되면 이전 값은 변경되지 않아야 한다', () => {
|
||||
const { result, rerender } = renderHook(({ value }) => usePrevious(value), {
|
||||
initialProps: { value: 5 },
|
||||
});
|
||||
|
||||
rerender({ value: 10 });
|
||||
expect(result.current).toBe(5);
|
||||
|
||||
rerender({ value: 10 });
|
||||
expect(result.current).toBe(5); // 여전히 처음 값
|
||||
});
|
||||
|
||||
it('undefined를 값으로 전달할 수 있어야 한다', () => {
|
||||
const { result, rerender } = renderHook(({ value }) => usePrevious(value), {
|
||||
initialProps: { value: undefined },
|
||||
});
|
||||
|
||||
expect(result.current).toBeUndefined();
|
||||
|
||||
rerender({ value: 'something' });
|
||||
expect(result.current).toBeUndefined();
|
||||
|
||||
rerender({ value: undefined });
|
||||
expect(result.current).toBe('something');
|
||||
});
|
||||
|
||||
it('0과 false를 정확히 추적해야 한다', () => {
|
||||
const { result, rerender } = renderHook(({ value }) => usePrevious(value), {
|
||||
initialProps: { value: 0 },
|
||||
});
|
||||
|
||||
rerender({ value: false });
|
||||
expect(result.current).toBe(0);
|
||||
|
||||
rerender({ value: 0 });
|
||||
expect(result.current).toBe(false);
|
||||
});
|
||||
|
||||
it('빈 배열과 객체를 추적할 수 있어야 한다', () => {
|
||||
const emptyArr = [];
|
||||
const emptyObj = {};
|
||||
|
||||
const { result: arrResult, rerender: arrRerender } = renderHook(
|
||||
({ arr }) => usePrevious(arr),
|
||||
{ initialProps: { arr: emptyArr } }
|
||||
);
|
||||
|
||||
arrRerender({ arr: [1, 2, 3] });
|
||||
expect(arrResult.current).toEqual([]);
|
||||
expect(arrResult.current).toBe(emptyArr);
|
||||
|
||||
const { result: objResult, rerender: objRerender } = renderHook(
|
||||
({ obj }) => usePrevious(obj),
|
||||
{ initialProps: { obj: emptyObj } }
|
||||
);
|
||||
|
||||
objRerender({ obj: { name: 'test' } });
|
||||
expect(objResult.current).toEqual({});
|
||||
expect(objResult.current).toBe(emptyObj);
|
||||
});
|
||||
});
|
||||
});
|
||||
242
com.twin.app.shoptime/src/hooks/usePreviousExample.jsx
Normal file
242
com.twin.app.shoptime/src/hooks/usePreviousExample.jsx
Normal file
@@ -0,0 +1,242 @@
|
||||
import { useState } from 'react';
|
||||
import usePrevious from './usePrevious';
|
||||
|
||||
/**
|
||||
* usePrevious 훅의 다양한 사용 예시를 보여주는 컴포넌트
|
||||
*/
|
||||
|
||||
// 예시 1: 단일 값 추적 - 카운터
|
||||
export function CounterExample() {
|
||||
const [count, setCount] = useState(0);
|
||||
const prevCount = usePrevious(count);
|
||||
|
||||
return (
|
||||
<div style={{ padding: '20px', border: '1px solid #ccc', marginBottom: '20px' }}>
|
||||
<h3>예시 1: 단일 값 추적 (카운터)</h3>
|
||||
<p>현재값: {count}</p>
|
||||
<p>이전값: {prevCount !== undefined ? prevCount : '초기값'}</p>
|
||||
<button onClick={() => setCount(count + 1)}>증가</button>
|
||||
<button onClick={() => setCount(count - 1)}>감소</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 예시 2: 다중 값 추적 (객체) - 사용자 정보
|
||||
export function UserFormExample() {
|
||||
const [name, setName] = useState('');
|
||||
const [age, setAge] = useState('');
|
||||
const [email, setEmail] = useState('');
|
||||
|
||||
const prev = usePrevious({ name, age, email });
|
||||
|
||||
const hasNameChanged = prev?.name !== name;
|
||||
const hasAgeChanged = prev?.age !== age;
|
||||
const hasEmailChanged = prev?.email !== email;
|
||||
|
||||
return (
|
||||
<div style={{ padding: '20px', border: '1px solid #ccc', marginBottom: '20px' }}>
|
||||
<h3>예시 2: 다중 값 추적 (객체) - 사용자 정보</h3>
|
||||
|
||||
<div style={{ marginBottom: '10px' }}>
|
||||
<label>
|
||||
이름:
|
||||
<input
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
style={{ marginLeft: '10px' }}
|
||||
/>
|
||||
{hasNameChanged && <span style={{ color: 'red', marginLeft: '10px' }}>변경됨</span>}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div style={{ marginBottom: '10px' }}>
|
||||
<label>
|
||||
나이:
|
||||
<input
|
||||
type="text"
|
||||
value={age}
|
||||
onChange={(e) => setAge(e.target.value)}
|
||||
style={{ marginLeft: '10px' }}
|
||||
/>
|
||||
{hasAgeChanged && <span style={{ color: 'red', marginLeft: '10px' }}>변경됨</span>}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div style={{ marginBottom: '10px' }}>
|
||||
<label>
|
||||
이메일:
|
||||
<input
|
||||
type="text"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
style={{ marginLeft: '10px' }}
|
||||
/>
|
||||
{hasEmailChanged && <span style={{ color: 'red', marginLeft: '10px' }}>변경됨</span>}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: '15px', padding: '10px', backgroundColor: '#f5f5f5' }}>
|
||||
<h4>이전 값</h4>
|
||||
<p>이름: {prev?.name || '(없음)'}</p>
|
||||
<p>나이: {prev?.age || '(없음)'}</p>
|
||||
<p>이메일: {prev?.email || '(없음)'}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 예시 3: 배열 값 추적 - 다중 숫자
|
||||
export function ArrayValuesExample() {
|
||||
const [a, setA] = useState(0);
|
||||
const [b, setB] = useState(0);
|
||||
const [c, setC] = useState(0);
|
||||
|
||||
const [prevA, prevB, prevC] = usePrevious([a, b, c]) || [];
|
||||
|
||||
return (
|
||||
<div style={{ padding: '20px', border: '1px solid #ccc', marginBottom: '20px' }}>
|
||||
<h3>예시 3: 배열 값 추적 - 다중 숫자</h3>
|
||||
|
||||
<div style={{ marginBottom: '15px' }}>
|
||||
<div>
|
||||
<label>
|
||||
A:
|
||||
<input
|
||||
type="number"
|
||||
value={a}
|
||||
onChange={(e) => setA(Number(e.target.value))}
|
||||
style={{ marginLeft: '10px' }}
|
||||
/>
|
||||
</label>
|
||||
<span style={{ marginLeft: '20px' }}>이전: {prevA !== undefined ? prevA : '초기값'}</span>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: '10px' }}>
|
||||
<label>
|
||||
B:
|
||||
<input
|
||||
type="number"
|
||||
value={b}
|
||||
onChange={(e) => setB(Number(e.target.value))}
|
||||
style={{ marginLeft: '10px' }}
|
||||
/>
|
||||
</label>
|
||||
<span style={{ marginLeft: '20px' }}>이전: {prevB !== undefined ? prevB : '초기값'}</span>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: '10px' }}>
|
||||
<label>
|
||||
C:
|
||||
<input
|
||||
type="number"
|
||||
value={c}
|
||||
onChange={(e) => setC(Number(e.target.value))}
|
||||
style={{ marginLeft: '10px' }}
|
||||
/>
|
||||
</label>
|
||||
<span style={{ marginLeft: '20px' }}>이전: {prevC !== undefined ? prevC : '초기값'}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 예시 4: 변동 감지 - 데이터 수정 여부 추적
|
||||
export function ChangeDetectionExample() {
|
||||
const [data, setData] = useState({
|
||||
title: 'React Hook Guide',
|
||||
description: 'usePrevious 훅 사용법',
|
||||
views: 1000,
|
||||
});
|
||||
|
||||
const prevData = usePrevious(data);
|
||||
|
||||
const changes = {
|
||||
titleChanged: prevData?.title !== data.title,
|
||||
descriptionChanged: prevData?.description !== data.description,
|
||||
viewsChanged: prevData?.views !== data.views,
|
||||
};
|
||||
|
||||
const hasAnyChanges = Object.values(changes).some((v) => v);
|
||||
|
||||
const handleUpdate = (field, value) => {
|
||||
setData((prev) => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ padding: '20px', border: '1px solid #ccc', marginBottom: '20px' }}>
|
||||
<h3>예시 4: 변동 감지 - 데이터 수정 여부 추적</h3>
|
||||
|
||||
<div style={{ marginBottom: '15px' }}>
|
||||
<label>
|
||||
제목:
|
||||
<input
|
||||
type="text"
|
||||
value={data.title}
|
||||
onChange={(e) => handleUpdate('title', e.target.value)}
|
||||
style={{ marginLeft: '10px', width: '200px' }}
|
||||
/>
|
||||
{changes.titleChanged && (
|
||||
<span style={{ color: 'orange', marginLeft: '10px' }}>수정됨</span>
|
||||
)}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div style={{ marginBottom: '15px' }}>
|
||||
<label>
|
||||
설명:
|
||||
<input
|
||||
type="text"
|
||||
value={data.description}
|
||||
onChange={(e) => handleUpdate('description', e.target.value)}
|
||||
style={{ marginLeft: '10px', width: '200px' }}
|
||||
/>
|
||||
{changes.descriptionChanged && (
|
||||
<span style={{ color: 'orange', marginLeft: '10px' }}>수정됨</span>
|
||||
)}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div style={{ marginBottom: '15px' }}>
|
||||
<label>
|
||||
조회수:
|
||||
<input
|
||||
type="number"
|
||||
value={data.views}
|
||||
onChange={(e) => handleUpdate('views', Number(e.target.value))}
|
||||
style={{ marginLeft: '10px' }}
|
||||
/>
|
||||
{changes.viewsChanged && (
|
||||
<span style={{ color: 'orange', marginLeft: '10px' }}>수정됨</span>
|
||||
)}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div style={{ padding: '10px', backgroundColor: hasAnyChanges ? '#fff3cd' : '#e8f5e9' }}>
|
||||
<p style={{ margin: '0' }}>
|
||||
<strong>
|
||||
상태: {hasAnyChanges ? '데이터가 변경되었습니다' : '모든 데이터가 저장되었습니다'}
|
||||
</strong>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 전체 예시 조합
|
||||
export function UsePreviousExamples() {
|
||||
return (
|
||||
<div style={{ padding: '20px', fontFamily: 'Arial, sans-serif' }}>
|
||||
<h1>usePrevious 훅 사용 예시</h1>
|
||||
<p>이전 값을 추적하는 다양한 방법을 보여주는 예시들입니다.</p>
|
||||
|
||||
<CounterExample />
|
||||
<UserFormExample />
|
||||
<ArrayValuesExample />
|
||||
<ChangeDetectionExample />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default UsePreviousExamples;
|
||||
Reference in New Issue
Block a user