【问题标题】:Component not updating on state change with React Router组件未使用 React Router 更新状态更改
【发布时间】:2021-04-24 20:02:47
【问题描述】:

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

应用组件

  • 已获取所有路由和数据
  • 使用 Reacts Context API,我用它来将我的获取传递给子组件
  • 低于 App 组件的基本形状。
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>
    )
}
 

【问题讨论】:

  • 能贴出上下文代码吗?
  • 如果我理解您的描述,您单击按钮调用getData 并且某些状态将在上下文中更新并重新呈现组件?请在您的问题中包含相关的上下文代码、提供程序等。当上下文更新时,您希望 ChildOfChild 做什么?
  • @ViniciusKatata 我为Appcomponent 添加了更多代码。尤其是useContext的信息。
  • 你用data做什么,好像没用过。
  • @DrewReese 谢谢,请给我一点时间,我将添加所有逻辑。

标签: reactjs react-context


【解决方案1】:

问题

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

建议

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

孩子

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

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

ChildOfChild

通过useContext 钩子将DataContext 添加到组件中。 使用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>

【讨论】:

  • 感谢您的深入回答。这正是我最终这样做的方式,我想知道是否没有其他方法可以使用 react-router 传递状态,因此我看不出有任何理由在链接中使用状态,因为无法更新状态。我还必须为子页面的item 的多个子元素重复find func。在此之上。我是这样做的 import { useParams } from "react-router-dom"; const{ dataId } = useParams(); 并将 dataId 包含在 useEffect 函数的依赖项中,因此它会在路由更改时更新。你还没有这样做。
  • @BenjaminK 由于组件直接由组件属性上的Route 渲染,因此它接收route props,其中match 就是其中之一。您当然可以使用useParams 挂钩来获取匹配参数。通过路由状态发送数据的问题在于它是非常瞬态的。它只存在于过渡期间,接收路由的组件必须保存它。我解释了为什么这个版本的传递状态不会被更新。
  • @BenjaminK 至于find 函数,如果这是一个常见的、重复的模式,那么这是一个很好的因式分解为实用函数的候选者,这样你就可以让你的代码更干(D不要R重复Y我们自己)。
【解决方案2】:

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

看看这里,它有一些小的拼写错误,但这里看起来还可以

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

【讨论】:

  • 感谢您为将其放入代码沙箱所做的所有努力,您是否介意解释一下您发现的错误以及修复它的方法。因为我不清楚。谢谢。
  • 就像我说的,这是类型错误,例如:...resh Data&lt;button /&gt; 应该是 ...resh Data&lt;/button&gt;&lt;div&gt;{item.name}&lt;div&gt; 没有结束标签 &lt;div&gt;{item.name}&lt;/div&gt;
  • 您的沙箱没有显示任何用户界面。并且键入错误仅存在于 StackOverflow 中,因为我在此处编写了简化的代码,因为我的生产示例要复杂得多。肯定不行。
  • Your sandbox doesn't show anything ui wise,没听懂
猜你喜欢
  • 2018-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-30
  • 2018-06-15
  • 1970-01-01
  • 2017-03-06
相关资源
最近更新 更多