React Hooks-引用在use内部不可用

时间:2019-01-24 11:52:37

标签: reactjs react-hooks

我正在使用ReactHooks。我正在尝试访问ref函数中User组件的useEffect。但是,尽管我将elRef.current作为第二个参数传递给null,但我得到的elRef.current的值是useEffect。但是我想参考div元素。但是useEffect的外部(函数体)的值ref可用。这是为什么 ?如何获得elRef.current中的useEffect值?

代码

import React, { Component, useState, useRef, useEffect } from "react";

const useFetch = url => {
  const [data, setData] = useState(null);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(
    () => {
      setIsLoading(true);
      fetch(url)
        .then(response => {
          if (!response.ok) throw Error(response.statusText);
          return response.json();
        })
        .then(json => {
          setIsLoading(false);
          setData(json.data);
        })
        .catch(error => {
          setIsLoading(false);
          setError(error);
        });
    },
    [url]
  );

  return { data, isLoading, error };
};

const User = ({ id }) => {
  const elRef = useRef(null);
  const { data: user } = useFetch(`https://reqres.in/api/users/${id}`);

  useEffect(() => {
    console.log("ref", elRef.current);
  }, [elRef.current]);
  if (!user) return null;
  return <div ref={elRef}>{user.first_name + " " + user.last_name}</div>;
};

class App extends Component {
  state = {
    userId: 1
  };

  handleNextClick = () => {
    this.setState(prevState => ({
      userId: prevState.userId + 1
    }));
  };

  handlePrevNext = () => {
    this.setState(prevState => ({
      userId: prevState.userId - 1
    }));
  };
  render() {
    return (
      <div>
        <button
          onClick={() => this.handlePrevClick()}
          disabled={this.state.userId === 1}
        >
          Prevoius
        </button>
        <button onClick={() => this.handleNextClick()}>Next</button>
        <User id={this.state.userId} />
      </div>
    );
  }
}

export default App;

Codesandbox link

谢谢!

5 个答案:

答案 0 :(得分:12)

您应该使用 useCallback 而不是reactjs docs中建议的useRef。

只要ref附加到其他节点,React都会调用该回调。

替换此:

df = pd.concat(dict_df).reset_index(level=1, drop=True).rename_axis('Dept').reset_index()

与此:

const elRef = useRef(null);
useEffect(() => {
    console.log("ref", elRef.current);
}, [elRef.current]);

答案 1 :(得分:1)

这是可预见的行为。

正如提到的@estus一样,您遇到了这个问题,因为第一次调用componentDidMount时,您会得到null(初始值),并且在下一个{{1} }更改,因为实际上引用仍然相同。

如果您需要考虑每个用户的更改,则应将elRef作为第二个参数传递给函数,以确保在更改用户时触发[user]

Here是更新的沙箱。

希望有帮助。

答案 2 :(得分:0)

useEffect可以替代componentDidMountcomponentDidUpdate。当参考为null时,在初始渲染时调用它。

如果在更改ref时仅应在后续渲染上调用它,则应为:

  useEffect(() => {
    if (!elRef.current)
      return;

    console.log("ref", elRef.current);
  }, [elRef.current]);

答案 3 :(得分:0)

useEffect用作componentDidMount和componentDidUpdate, 在组件安装时,您添加了一个条件:

if (!user) return null;
return <div ref={elRef}>{user.first_name + " " + user.last_name}</div>;

由于在挂载时出现上述情况,因此您没有用户,因此它返回null,并且div未挂载在要添加ref的DOM中,因此在useEffect内部,您没有得到elRef的当前值值,因为它没有呈现。

然后在div安装在dom中时单击下一步,您将获得elRef.current的值。

答案 4 :(得分:0)

这里的假设是useEffect需要检测对ref.current的更改,因此需要在依赖项列表中包含refref.current。我认为这是由于es-lint过于-脚。

实际上,useEffect的全部要点在于,它保证在渲染完成且DOM准备就绪之前不运行。这就是它处理副作用的方式。

因此,在执行useEffect时,我们可以确定设置了elRef.current

代码的问题在于,直到填充<div ref={elRef}...>之后,您才使用user运行渲染器。因此,您要elRef引用的DOM节点尚不存在。这就是为什么您获得null日志记录的原因-与依赖关系无关。


顺便说一句:一种可能的替代方法是在效果挂钩中填充div:

useEffect(
  () => {
    if(!user) return;
    elRef.current.innerHTML = `${user.first_name} ${user.last_name}`;
  }, [user]
);

这样,用户组件中的if (!user) return null;行就不需要了。删除它,并保证elRef.current从一开始就填充div节点。