Вопросы по инициализации useState с помощью Date.now () - PullRequest
3 голосов
/ 18 июня 2020

Я использую API для получения последних сообщений из Firestore. API выглядит примерно так:

function loadFeedPosts(
  createdAtMax,
  limit
) {
  return db
    .collection("posts")
    .orderBy("createdAt", "desc")
    .where("createdAt", "<", createdAtMax)
    .limit(limit)
    .get()
    .then(getDocsFromSnapshot)
}

createdAtMax - это время, которое нам нужно указать для получения сообщений.

А у меня есть компонент Feed. Это выглядит так.



function Feed() {
  const [posts, setPosts] = useState([])
  const [currentTime, setCurrentTime] = useState(Date.now())
  const [limit, setLimit] = useState(3)

  useEffect(() => {
    loadFeedPosts(currentTime, limit).then(posts => {
      setPosts(posts)
    })
  }, [currentTime, limit])

  return (
    <div className="Feed">
      <div className="Feed_button_wrapper">
        <button className="Feed_new_posts_button icon_button">
          View 3 New Posts
        </button>
      </div>

      {posts.map(post => (
        <FeedPost post={post} />
      ))}

      <div className="Feed_button_wrapper">
        <button
          className="Feed_new_posts_button icon_button"
          onClick={() => {
            setLimit(limit + 3)
          }}
        >
          View More
        </button>
      </div>
    </div>
  )
}

Итак, когда люди нажимают кнопку View More, он go получит еще 3 сообщения.

Мой вопрос в том, что, поскольку нажатие на кнопку View More запускает повторный рендеринг, я предполагаю, что строка const [currentTime, setCurrentTime] = useState(Date.now()) будет запущена снова, делает ли Date.now(), которое служит начальным значением для currentTime снова оценивать каждый повторный рендеринг?

Я тестировал это сам, выйдя из системы currentTime, и казалось, что не обновляется для каждого рендера. Согласно React do c https://reactjs.org/docs/hooks-reference.html#lazy -initial-state , я думал, что только при использовании начального состояния Lazy initialState вычисляется только один раз. Но здесь я не использую ленивое начальное состояние, почему оно не вычисляется каждый раз при повторном рендеринге компонента?

Хотя предполагаемое поведение начального состояния currentTime точно остается таким же для каждого повторного рендеринга я просто сбит с толку, почему он не пересчитывался для каждого повторного рендеринга, поскольку я не использую ленивое начальное состояние

Ответы [ 2 ]

2 голосов
/ 19 июня 2020

useState() инициализирует состояние с помощью mount, он запускается непосредственно перед первым рендером.

Из do c:

Во время начальный рендер , возвращаемое состояние (состояние) совпадает со значением, переданным в качестве первого аргумента (initialState).

Зачем нам нужно ленивое начальное состояние?

Допустим, мы получаем наше начальное состояние после некоторой функции высокой сложности. Давайте попробуем без ленивого подхода:

const A = () => {
  const [a, setA] = useState("blabla")
  const processedA = processA(a) // Wait! this will run on every render!, 
  // so i need to find a way to prevent to be processed after first render.

  return ...
}

Чтобы реализовать функцию, которая запускается только один раз, я должен отслеживать рендеры с помощью useRef:

const A = () => {
  const [a, setA] = useState("blabla")
  let processedA

  const willMount = useRef(true);
  if (willMount.current) {
    processedA = processA(a)
  }

  useEffect(() => {
    willMount.current = false;
  }), []);

 return ...
}

Какой уродливый подход, не правда ли? ?

Другой подход может заключаться в использовании useEffect, но на этот раз он будет работать на втором рендере после монтирования. Так что у меня будет ненужный первый рендер:

const A = () => {
  const [a, setA] = useState("blabla")

  useEffect(()=>{
    const processedA = processA(a)
    setA(processedA)
  }, [a])

  return ...
}

2 голосов
/ 19 июня 2020

Используя useState, вы предоставляете только начальное значение, поскольку do c о хуке useState говорит:

Что мы передаем в useState как аргумент? Единственным аргументом хука useState () является начальное состояние.

Причина, по которой у нас ленивая инициализация, заключается в том, что в последующих рендерах initialValue будет игнорироваться, и если это результат дорогостоящее вычисление, которое вы можете избежать, чтобы повторно выполнить его.

const [value, setValue] = useState(expensiveComputation());
// expensiveComputation will be executed at each render and the result disregarded

const [value, setValue] = useState(() => expensiveComputation());
// expensiveComputation will be executed only at the first render
...