组件不会随着 React Router 的状态变化而更新

时间:2021-04-24 20:02:47

标签: reactjs react-context

我正在使用 React 和 React Router。我在 App.js 中定义了所有数据获取和路由。 我正在单击嵌套子组件 <ChildOfChild /> 中的按钮,当单击按钮(使用 Context API 向下传递函数)时刷新我的数据,并在我的顶级组件 App.js 中发生获取请求(我有一个console.log 那里,所以它肯定会在点击时获取)。但是刷新后的数据状态永远不会到达 <ChildOfChild /> 组件。相反,它会刷新旧状态。我究竟做错了什么。我如何确保 <Link> 内的状态在状态更新时刷新。 我希望 item.name 值在按钮点击时更新。

应用组件

  • 拥有所有的路由和数据获取
  • 使用 Reacts Context API,我用它来将我的抓取传递给子组件
  • 在应用组件的基本形状下方。
import React, {useEffect, useState} from "react";

export const FetchContext = React.createContext();
export const DataContext = React.createContext();

const App = () => {

const [data, setData] = useState([false, "idle", [], null]);

  useEffect(() => {
    fetchData()
  }, []);

  const fetchData = async () => {
    setData([true, "fetching", [], null]);
    try {
      const res = await axios.get(
        `${process.env.REACT_APP_API}/api/sample/`,
        {
          headers: { Authorization: `AUTHTOKEN` },
        }
      );
      console.log("APP.js - FETCH DATA", res.data)
      setData([false, "fetched", res.data, null]);
    } catch (err) {
      setData([false, "fetched", [], err]);
    }
  };

 return (
  <Router>
    <DataContext.Provider value={data}>
      <FetchContext.Provider value={fetchData}>
        <Switch>
          <Route exact path="/sample-page/" component={Child} />
          <Route exact path="/sample-page/:id" component={ChildOfChild} />
        </Switch>
      </FetchContext.Provider>
    </DataContext.Provider>
  </Router>
  )
}

子组件

import { DataContext } from "../App";

const Child = () => {
  const [isDataLoading, dataStatus, data, dataFetchError] = useContext(DataContext);
  const [projectsData, setProjectsData] = useState([]);
  {
    data.map((item) => (
      <Link
        to={{
          pathname: `/sampe-page/${item.id}`,
          state: { item: item },
        }}
      >
        {item.name}
      </Link>
    ));
  }

子组件的子组件

import { FetchContext } from "../App";

const ChildOfChild = (props) => {
    const getData = useContext(FetchContext);
    const [item, setItem] = useState({});
    const [isItemLoaded, setIsItemLoaded] = useState(false);

    useEffect(() => {
        if (props.location.state.item) {
          setItem(props.location.state.item);
          setIsItemLoaded(true);
        }
    }, [props]);

    return (
        <div>
            <button onClick={() => getData()}Refresh Data</button>
            <div>{item.name}</div>
        </div>
    )
}
 

2 个答案:

答案 0 :(得分:0)

问题

ChildOfChild 呈现的特定数据项仅通过从 "/sample-page/""/sample-page/:id" 的路由转换发送,并且 ChildOfChild 在本地状态缓存它的副本。更新 data 中的 DataContext 状态不会更新 ChildOfChild 持有的本地化副本。

建议

由于您已经在唯一标识它的路径上呈现 ChildOfChild,(回想一下 Child PUSHed to "/sample-page/${item.id}")您可以使用该路线的 idDataContext 访问特定数据项。无需在路由状态下也发送整个数据项。

孩子

只需按项目 id 链接到新页面。

<Link to={`/sampe-page/${item.id}`}>{item.name}</Link>

ChildOfChild

通过 DataContext 钩子将 useContext 添加到组件中。 使用 props.match 访问路由的 id 匹配参数。

import { FetchContext } from "../App";
import { DataContext } from "../App";

const ChildOfChild = (props) => {
  const getData = useContext(FetchContext);
  const [,, data ] = useContext(DataContext);

  const [item, setItem] = useState({});
  const [isItemLoaded, setIsItemLoaded] = useState(false);

  useEffect(() => {
    const { match: { params: { id } } } = props;
    if (id) {
      setItem(data.find(item => item.id === id));
      setIsItemLoaded(true);
    }
  }, [data, props]);

  return (
    <div>
      <button onClick={getData}Refresh Data<button />
      <div>{item?.name}<div>
    </div>
  )
}

useEffect 将确保当来自上下文或道具的 data 中的一个或两个更新时,item 状态将更新为最新的 dataid 参数。

关于使用 Switch 组件的旁注,路由路径顺序和特异性很重要。 Switch 将匹配并呈现与路径匹配的第一个组件。您将希望在 less 特定路径之前对 more 特定路径进行排序。这也使您无需为每个 exact 添加 Route 属性。现在,Switch 可以尝试在不太具体的路径“/sample-page”之前匹配更具体的路径“/sample-page/123”。

<Router>
  <DataContext.Provider value={data}>
    <FetchContext.Provider value={fetchData}>
      <Switch>
        <Route path="/sample-page/:id" component={ChildOfChild} />
        <Route path="/sample-page/" component={Child} />
      </Switch>
    </FetchContext.Provider>
  </DataContext.Provider>
</Router>

答案 1 :(得分:-1)

我刚刚在这里重写了您的代码,我使用了 randomuser.me/api 来获取数据

看看这里,它有一些拼写错误,但在这里看起来不错

https://codesandbox.io/s/modest-paper-nde5c?file=/src/Child.js