React: не обрабатывать useEffect при первой отрисовке компонента

есть такой компонент:

// компонент "выпадающий список"
const DropdownList = (props: IProps_DropdownList) => {
    // контроль состояний
    const [values, setValues] = useState(props.values);           // выбранные элементы 

    // эффект: изменен выбор элементов выпадающего списка
    useEffect(() => {
        props.onChange?.(values);
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [values, props.onChange])

    // остальная логика
    return <></>
}

в компоненте при разных ситуациях изменяется значение values

это значение должно затем передаться вовне

чтоб код передачи не таскать по всему компоненту использую useEffect и когда знамение меняется, то оно передаётся наружу через вызов колбэка props.onChange?.(values)

При этом при создании компонента, установки состояния values сразу же срабатывает и useEffect, т.е. я всегда дергается колбэк, а мне этого не надо.

Как можно избежать такого поведения?

При этом очень не хотелось бы:

  • использовать дополнительные состояния использовать колбэк в разных
  • частях компонента

Ответы (2 шт):

Автор решения: ksa

не обрабатывать useEffect при первой отрисовке компонента

Например вот так...

const DropdownList = props => {
  const ref = React.useRef(null);
  const [values, setValues] = React.useState(props.values);
  React.useEffect(_ => {
    console.log('useEffect')
    if (ref.current) {
      console.log('onChange')
      if (props.onChange) props.onChange(values);
    } else {
      ref.current = true
      console.log('Ничего не случилось...')
    }
  }, [values, props.onChange])
  const act = e => setValues(e.target.value)
  return <div>
    <input onChange={act} value={values} />
  </div>
}
const App = props => {
  const [val, setVal] = React.useState(0);

  return <div>
    <DropdownList values={val} />
  </div>
}



const domContainer = document.querySelector('#like_button_container');
const root = ReactDOM.createRoot(domContainer);
root.render(<App />);
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<div id="like_button_container"></div>

→ Ссылка
Автор решения: SaNFeeD

У себя в проекте использую самописный хук.

const useIsFirstRender = () => {
  const isFirst = useRef(true)

  if (isFirst.current) {
    isFirst.current = false
    return true
  }

  return isFirst.current
}

В компонентах используется так.

const Component = (props) => {
  const [values, setValues] = useState(props.values);
  const isFirst = useIsFirstRender()
   
  useEffect(() => {
    if(!isFirst) props.onChange?.(values);
  }, [values])
  
  return (<div>Component</div>)
}
→ Ссылка