getSnapshotBeforeUpdate с использованием перехватчиков реакции

Как я могу реализовать ту же логику, которую дает мне getSnapshotBeforeUpdate, используя перехватчики реакции?


person Luke    schedule 29.11.2018    source источник


Ответы (4)


Согласно часто задаваемым вопросам о хуках React, пока нет возможности реализовать методы жизненного цикла getSnapshotBeforeUpdate и ComponentDidCatch с хуками

Охватывают ли хуки все варианты использования классов?

Наша цель состоит в том, чтобы хуки как можно скорее охватили все варианты использования классов. Пока нет эквивалентов хуков для необычных жизненных циклов getSnapshotBeforeUpdate и componentDidCatch, но мы планируем добавить их в ближайшее время.

Для хуков еще очень рано, поэтому некоторые интеграции, такие как поддержка DevTools или типизация Flow / TypeScript, могут быть еще не готовы. Некоторые сторонние библиотеки на данный момент могут быть несовместимы с хуками.

person Shubham Khatri    schedule 30.11.2018

Мы не можем получить данные моментального снимка ни в одном из хуков (useLayoutEffect или useEffect), так как оба будут давать обновленные значения DOM к моменту их запуска, лучшее место для захвата данных - непосредственно перед установкой состояния. например, здесь я фиксирую положение прокрутки перед установкой состояния.

function ChatBox(props){

  const [state, setState] = useState({chatFetched:[],isFetching:false});

  const listRef = useRef();
  const previousScrollDiff = useRef(0);
  
  // on mount 
  useEffect(()=>{
    getSomeMessagesApi().then(resp=>{
      const chatFetched = [...state.chatFetched,...resp];
      setState({chatFetched});
    })
  },[]);

  useLayoutEffect(()=>{
   // use the captured snapshot here
   listRef.current.scrollTop = listRef.current.scrollHeight - previousScrollDiff.current;

  },[state.chatFetched])
  
  useEffect(()=>{

    // don't use captured snapshot here ,will cause jerk effect in scroll

  },[state.chatFetched]);


  const onScroll = (event) => {
    const topReached = (event.target.scrollTop === 0);

    if(topReached && !state.isFetching){

      setState({...state, isFetching:true});

      getSomeMessagesApi().then(resp=>{
        const chatFetched = [...resp,...state.chatFetched];

        // here I am capturing the data ie.., scroll position

        previousScrollDiff.current = listRef.current.scrollHeight -listRef.current.scrollTop;
        setState({chatFetched, isFetching:false});
      })
    }
  }

  return (  
    <div className="ui container">
      <div 
        className="ui container chat list" 
        style={{height:'420px', width:'500px',overflow:'auto'}}
        ref={listRef}
        onScroll={onScroll}
        >
          {state.chatFetched.map((message)=>{
           return <ChatLi data ={message} key ={message.key}></ChatLi>
          })}
      </div>  
    </div>
   ); 
};

мы также можем использоватьMemo для захвата данных до того, как произойдет обновление dom,

function ChatBox(props){

  const [state, setState] = useState({chatFetched:[],isFetching:false});

  const listRef = useRef();
  const previousScrollDiff = useRef(0);
  
  // on mount 
  useEffect(()=>{
    getSomeMessagesApi().then(resp=>{
      const chatFetched = [...state.chatFetched,...resp];
      setState({chatFetched});
    })
  },[]);

  useLayoutEffect(()=>{
   // use the captured snapshot here
   listRef.current.scrollTop = listRef.current.scrollHeight - previousScrollDiff.current;

  },[state.chatFetched])
  
  useEffect(()=>{

    // don't use captured snapshot here ,will cause jerk effect in scroll

  },[state.chatFetched]);

  useMemo(() => {
   // caputure dom info in use effect
    if(scrollUl.current){
       previousScrollDiff.current = scrollUl.current.scrollHeight - scrollUl.current.scrollTop;
    }
    
  }, [state.chatFetched]);

  const onScroll = (event) => {
    const topReached = (event.target.scrollTop === 0);

    if(topReached && !state.isFetching){

      setState({...state, isFetching:true});

      getSomeMessagesApi().then(resp=>{
        const chatFetched = [...resp,...state.chatFetched];
        setState({chatFetched, isFetching:false});
      })
    }
  }

  return (  
    <div className="ui container">
      <div 
        className="ui container chat list" 
        style={{height:'420px', width:'500px',overflow:'auto'}}
        ref={listRef}
        onScroll={onScroll}
        >
          {state.chatFetched.map((message)=>{
           return <ChatLi data ={message} key ={message.key}></ChatLi>
          })}
      </div>  
    </div>
   ); 
};

В приведенных выше примерах я пытаюсь сделать то же самое, что показано здесь, в документе реакции getSnapshotBeforeUpdate.

person MOHANTEJA    schedule 19.08.2020

Краткий ответ: для этого нет ловушки реакции! Но мы можем создать индивидуальный!

Это использует useEffect() и useLayoutEffect()! Ведь они ключевые элементы!

Последний пример - последний! Поэтому обязательно проверьте его (Эквивалент наших пользовательских хуков).

useEffect () и useLayoutEffect ()

useEffect = ›useEffect выполняется асинхронно и после визуализации отображается на экране.

  1. Вы каким-то образом вызываете рендеринг (изменение состояния или родительский рендеринг)
  2. React отображает ваш компонент (называет его)
  3. Экран визуально обновлен
  4. ТОГДА запускается useEffect

useEffect () = ›render () =› dom mutation = ›repaint =› useEffect () [доступ к новому состоянию dom] (изменение dom напрямую) = ›repaint

== ›Значение useEffect () похоже на comonentDidUpdate()!

useLayoutEffect = ›useLayoutEffect, с другой стороны, запускается синхронно после рендеринга, но до обновления экрана. Это идет:

  1. Вы каким-то образом вызываете рендеринг (изменение состояния или родительский рендеринг)
  2. React отображает ваш компонент (называет его)
  3. useLayoutEffect запускается, а React ожидает его завершения.
  4. Экран визуально обновлен

useLayoutEffect () = ›render =› dom mutation [detached] = ›useLayoutEffec () [доступ к новому состоянию dom] (mutate dom) =› перерисовать (зафиксировать, прикрепить)

=== ›Значение useLayoutEffect() запускать как getSnapshotBeforeUpdate()

Зная это! Мы можем создать собственные хуки, которые позволят нам делать такие вещи, как getSnapshotBeforeUpdate() и didComponentUpdate().

Таким примером может быть обновление прокрутки для автоматического обновления в приложениях чата!

usePreviousPropsAndState ()

Подобно хуку usePrevious(), упомянутому в как получить предыдущую опору и состояние

Вот реализация хука для сохранения и получения предыдущих реквизитов и состояния!

const usePrevPropsAndState = (props, state) => {
  const prevPropsAndStateRef = useRef({ props: null, state: null })
  const prevProps = prevPropsAndStateRef.current.props
  const prevState = prevPropsAndStateRef.current.state

  useEffect(() => {
    prevPropsAndStateRef.current = { props, state }
  })

  return { prevProps, prevState }
}

Мы видим, как нам нужно передать реквизиты и объект состояния!

То, что вы проходите, - это то, что вы получаете! Так что с ним легко работать! Объект будет хорошо!

useGetSnapshotBeforeUpdate и useComponentDidUpdate

Здесь полное решение или реализация

const useGetSnapshotBeforeUpdate = (cb, props, state) => {
  // get prev props and state
  const { prevProps, prevState } = usePrevPropsAndState(props, state)

  const snapshot = useRef(null)


// getSnapshotBeforeUpdate (execute before the changes are comitted for painting! Before anythingg show on screen) - not run on mount + run on every update
  const componentJustMounted = useRef(true)
  useLayoutEffect(() => {
    if (!componentJustMounted.current) { // skip first run at mount
           snapshot.current = cb(prevProps, prevState)  
    }
    componentJustMounted.current = false
  })


 // ________ a hook construction within a hook with closure __________
 const useComponentDidUpdate = cb => {
    // run after the changes are applied (commited) and apparent on screen
    useEffect(() => {
      if (!componentJustMounted.current) { // skip first run at mount
        cb(prevProps, prevState, snapshot.current)
      }
    })
  }
  // returning the ComponentDidUpdate hook!
  return useComponentDidUpdate
}

Вы можете заметить, как мы построили крючок внутри другого крючка! Используйте закрытие! И доступ к элементам напрямую! И соединяем два крючка!

фаза предварительной фиксации и фаза фиксации (и хуки эффектов)

Я использовал эти термины! Что это на самом деле означает?

введите описание изображения здесь

пример класса

Из документа

class ScrollingList extends React.Component {
  constructor(props) {
    super(props);
    this.listRef = React.createRef();
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    // Are we adding new items to the list?
    // Capture the scroll position so we can adjust scroll later.
    if (prevProps.list.length < this.props.list.length) {
      const list = this.listRef.current;
      return list.scrollHeight - list.scrollTop;
    }
    return null;
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    // If we have a snapshot value, we've just added new items.
    // Adjust scroll so these new items don't push the old ones out of view.
    // (snapshot here is the value returned from getSnapshotBeforeUpdate)
    if (snapshot !== null) {
      const list = this.listRef.current;
      list.scrollTop = list.scrollHeight - snapshot;
    }
  }

  render() {
    return (
      <div ref={this.listRef}>{/* ...contents... */}</div>
    );
  }
}

Эквивалент наших кастомных крючков

const App = props => {
  // other stuff ...

  const useComponentDidUpdate = useGetSnapshotBeforeUpdate(
    (prevProps, prevState) => {
      if (prevProps.list.length < props.list.length) {
        const list = listRef.current;
        return list.scrollHeight - list.scrollTop;
      }
      return null;
    },
    props,
    state
  )

  useComponentDidUpdate((prevProps, prevState, snapshot) => {
    if (snapshot !== null) {
      const list = listRef.current;
      list.scrollTop = list.scrollHeight - snapshot;
    }
  })

  // rest ...
}

useEffectLayout () в хук useGetSnapshotBeforeUpdate выполнится первым!

useEffect () в useComponentDidUpdate будет выполняться после!

Как только что было показано на схеме жизненного цикла!

person Mohamed Allal    schedule 06.07.2021