環境
React:v18.3.1
Tips
開発環境で useLayoutEffect と useEffect が2回実行されるのを防ぐには、
main.tsx の <React.StrictMode> のタグをコメントアウトすれば良い。
実行される順番を確認
再描画時に常に実行される場合
App.tsxの内容
import { ReactNode, useEffect, useLayoutEffect } from 'react';
import Parent from './Parent.tsx';
function App(): ReactNode {
useLayoutEffect(() => {
console.log('App useLayoutEffect');
});
useEffect(() => {
console.log('App useEffect');
});
return (
<>
あいうえお
<Parent />
</>
);
}
export default App;
Parent.tsxの内容
import {
ReactNode, useEffect, useLayoutEffect, useState,
} from 'react';
import Child1 from './Child1.tsx';
function Parent(): ReactNode {
const [child1Value, setChild1Value] = useState<string>('Child1の初期値');
const [parentValue1, setParentValue1] = useState<string>('Parentの初期値');
function parentBtnClickFunc() {
alert('parentBtnClickFunc');
setChild1Value('親からStateが変更されました');
}
useLayoutEffect(() => {
console.log('Parent useLayoutEffect');
});
useEffect(() => {
console.log('Parent useEffect');
});
return (
<>
<div>
親:
{parentValue1}
</div>
<button type="button" onClick={parentBtnClickFunc}>
親ボタン
</button>
<Child1 childProp={child1Value} parentStateFunc={setParentValue1} />
</>
);
}
export default Parent;
Child1.tsxの内容
import { ReactNode, useEffect, useLayoutEffect } from 'react';
type Child1Props = {
childProp: string,
parentStateFunc: Function
};
function Child1({ childProp, parentStateFunc }: Child1Props): ReactNode {
function childBtnClickFunc(): void {
alert('childBtnClickFunc');
parentStateFunc('子からStateが変更されました');
}
useLayoutEffect(() => {
console.log('Child1 useLayoutEffect');
});
useEffect(() => {
console.log('Child1 useEffect');
});
return (
<>
<div>
子:
{childProp}
</div>
<button type="button" onClick={childBtnClickFunc}>
子ボタン
</button>
</>
);
}
export default Child1;
結果(コンソールの表示内容)
// ページ表示後。
Child1 useLayoutEffect
Parent useLayoutEffect
App useLayoutEffect
Child1 useEffect
Parent useEffect
App useEffect
// 親ボタンを押下し、子のProp(親のState)を更新後。
Child1 useLayoutEffect
Parent useLayoutEffect
Child1 useEffect
Parent useEffect
// 子ボタンを押下し、親のStateを更新後。
Child1 useLayoutEffect
Parent useLayoutEffect
Child1 useEffect
Parent useEffect
初期描画時にのみ実行される場合
App.tsxの内容
import { ReactNode, useEffect, useLayoutEffect } from 'react';
import Parent from './Parent.tsx';
function App(): ReactNode {
useLayoutEffect(() => {
console.log('App useLayoutEffect');
}, []);
useEffect(() => {
console.log('App useEffect');
}, []);
return (
<>
あいうえお
<Parent />
</>
);
}
export default App;
Parent.tsxの内容
import {
ReactNode, useEffect, useLayoutEffect, useState,
} from 'react';
import Child1 from './Child1.tsx';
function Parent(): ReactNode {
const [child1Value, setChild1Value] = useState<string>('Child1の初期値');
const [parentValue1, setParentValue1] = useState<string>('Parentの初期値');
function parentBtnClickFunc() {
alert('parentBtnClickFunc');
setChild1Value('親からStateが変更されました');
}
useLayoutEffect(() => {
console.log('Parent useLayoutEffect');
}, []);
useEffect(() => {
console.log('Parent useEffect');
}, []);
return (
<>
<div>
親:
{parentValue1}
</div>
<button type="button" onClick={parentBtnClickFunc}>
親ボタン
</button>
<Child1 childProp={child1Value} parentStateFunc={setParentValue1} />
</>
);
}
export default Parent;
Child1.tsxの内容
import { ReactNode, useEffect, useLayoutEffect } from 'react';
type Child1Props = {
childProp: string,
parentStateFunc: Function
};
function Child1({ childProp, parentStateFunc }: Child1Props): ReactNode {
function childBtnClickFunc(): void {
alert('childBtnClickFunc');
parentStateFunc('子からStateが変更されました');
}
useLayoutEffect(() => {
console.log('Child1 useLayoutEffect');
}, []);
useEffect(() => {
console.log('Child1 useEffect');
}, []);
return (
<>
<div>
子:
{childProp}
</div>
<button type="button" onClick={childBtnClickFunc}>
子ボタン
</button>
</>
);
}
export default Child1;
結果(コンソールの表示内容)
// ページ表示後。
Child1 useLayoutEffect
Parent useLayoutEffect
App useLayoutEffect
Child1 useEffect
Parent useEffect
App useEffect
// 親ボタンを押下し、子のProp(親のState)を更新後。
※ 追加で表示されない。
// 子ボタンを押下し、親のStateを更新後。
※ 追加で表示されない。
特定のStateが更新された時に実行される場合
App.tsxの内容
import { ReactNode, useEffect, useLayoutEffect } from 'react';
import Parent from './Parent.tsx';
function App(): ReactNode {
useLayoutEffect(() => {
console.log('App useLayoutEffect');
}, []);
useEffect(() => {
console.log('App useEffect');
}, []);
return (
<>
あいうえお
<Parent />
</>
);
}
export default App;
Parent.tsxの内容
import {
ReactNode, useEffect, useLayoutEffect, useState,
} from 'react';
import Child1 from './Child1.tsx';
function Parent(): ReactNode {
const [child1Value, setChild1Value] = useState<string>('Child1の初期値');
const [parentValue1, setParentValue1] = useState<string>('Parentの初期値');
function parentBtnClickFunc() {
alert('parentBtnClickFunc');
setChild1Value('親からStateが変更されました');
}
useLayoutEffect(() => {
console.log('Parent useLayoutEffect');
}, [parentValue1]);
useEffect(() => {
console.log('Parent useEffect');
}, [parentValue1]);
return (
<>
<div>
親:
{parentValue1}
</div>
<button type="button" onClick={parentBtnClickFunc}>
親ボタン
</button>
<Child1 childProp={child1Value} parentStateFunc={setParentValue1} />
</>
);
}
export default Parent;
Child1.tsxの内容
import { ReactNode, useEffect, useLayoutEffect } from 'react';
type Child1Props = {
childProp: string,
parentStateFunc: Function
};
function Child1({ childProp, parentStateFunc }: Child1Props): ReactNode {
function childBtnClickFunc(): void {
alert('childBtnClickFunc');
parentStateFunc('子からStateが変更されました');
}
useLayoutEffect(() => {
console.log('Child1 useLayoutEffect');
}, [childProp]);
useEffect(() => {
console.log('Child1 useEffect');
}, [childProp]);
return (
<>
<div>
子:
{childProp}
</div>
<button type="button" onClick={childBtnClickFunc}>
子ボタン
</button>
</>
);
}
export default Child1;
結果(コンソールの表示内容)
// ページ表示後。
Child1 useLayoutEffect
Parent useLayoutEffect
App useLayoutEffect
Child1 useEffect
Parent useEffect
App useEffect
// 親ボタンを押下し、子のProp(親のState)を更新後。
Child1 useLayoutEffect
Child1 useEffect
// 子ボタンを押下し、親のStateを更新後。
Parent useLayoutEffect
Parent useEffect
useState で fetch してState更新
Child1.tsxの内容
import { ReactNode, useEffect, useLayoutEffect } from 'react';
type Child1Props = {
childProp: string,
parentStateFunc: Function
};
function Child1({ childProp, parentStateFunc }: Child1Props): ReactNode {
const [showText, setShowText] = useState('初期値');
const fetchData = async () => {
const response = await fetch('http://localhost:3001');
const data = await response.text();
return data;
};
useEffect(() => {
fetchData().then((text:string) => {
setShowText(text);
});
}, []);
return (
<>
<div>
子:
{childProp}
</div>
<div>
{showText}
</div>
</>
);
}
export default Child1;