react-transition-groupでリストを表示するときはkeyにindexを設定するのはやめよう
リストで表示したものにトランジションを適用したい
こんなやつをやりたかった
なんかこうなる
🤔
コードを比較
styles.ts
については一番下に書いておきます.
変な挙動をするコード
import React, { Fragment, useState, useCallback, useMemo } from "react"; import { Container, AnimationBox, FormContainer, Input, Submit } from "./styled"; export const ListTransition = () => { const [state, setState] = useState<string[]>(["hoge", "huga"]); const [value, setValue] = useState<string>(""); const onAdd = useCallback(() => { if (value.length <= 0) return; setState(ls => [...ls, value]); setValue(""); }, [value]); const onRemove = useCallback((s: string) => setState(l => l.filter(el => el !== s)), []); const disabled = useMemo(() => value.length <= 0, [value]); return ( <Fragment> <Container> {state.map((s, idx) => ( <AnimationBox key={idx} unmountOnExit timeout={800} onClick={() => onRemove(s)}> {s} </AnimationBox> ))} </Container> <FormContainer> <Input value={value} onChange={e => setValue(e.target.value)} /> <Submit onClick={onAdd} disabled={disabled}> Add Item </Submit> </FormContainer> </Fragment> ); };
想定している動きをするコード
import React, { Fragment, useState, useCallback, useMemo } from "react"; import { Container, AnimationBox, FormContainer, Input, Submit } from "./styled"; export const ListTransition = () => { const [state, setState] = useState<string[]>(["hoge", "huga"]); const [value, setValue] = useState<string>(""); const onAdd = useCallback(() => { if (value.length <= 0) return; setState(ls => [...ls, value]); setValue(""); }, [value]); const onRemove = useCallback((s: string) => setState(l => l.filter(el => el !== s)), []); const disabled = useMemo(() => value.length <= 0, [value]); return ( <Fragment> <Container> {state.map(s => ( <AnimationBox key={s} unmountOnExit timeout={800} onClick={() => onRemove(s)}> {s} </AnimationBox> ))} </Container> <FormContainer> <Input value={value} onChange={e => setValue(e.target.value)} /> <Submit onClick={onAdd} disabled={disabled}> Add Item </Submit> </FormContainer> </Fragment> ); };
原因
keyをindexにしていたからでした.keyが変わるので最後のコンポーネントが消えたと認識されてしまってトランジションが最後のコンポーネントにしか効かなかったというオチでした.
styles.ts
import styled from "styled-components"; import { TransitionGroup } from "react-transition-group"; import transition from "styled-transition-group"; export const Container = styled(TransitionGroup)` position: absolute; top: 0px; display: flex; width: 300px; overflow-x: scroll; margin: 12px; `; export const AnimationBox = transition.p` padding: 8px 16px; font-size: 13px; border: solid 1px #c1c1c1; margin: 0 8px; border-radius: 3px; background-color: #c1c1c1; color: white; cursor: pointer; &:enter { opacity: 0; } &:enter-active { opacity: 1; transition: all 0.8s ease-out; } &:exit { opacity: 1; } &:exit-active { opacity: 0; transition: all 0.8s ease-out; } `; export const FormContainer = styled.div` display: flex; margin-top: 56px; `; export const Input = styled.input``; export const Submit = styled.button``;