在React-Admin中更新上下文值会引发路由历史警告。

huangapple go评论67阅读模式
英文:

Updating context value in react-admin causes a router history warning

问题

我正在使用上下文(Context)来存储一个全局的信息,该信息在从链接组件导航网页应用时进行更新。出现一个奇怪的问题,当我更新上下文数据时,在控制台中出现警告:“警告:无法更改”。我的数据中没有历史对象,只有一个值和一个用于更新的函数。可能是什么原因引起了这个问题?

这是我的设置:

const AppContext = createContext({
    appData: {},
    setAppData: () => {},
});

const App = () => {
  
  const [appData, setAppData] = useState({
    foo: null,
  });
  
  return (
    <AppContext.Provider value={[appData, setAppData]}>
        <Admin title="My Admin App">
          <Resource name="foo" show={ShowFoo} />
        </Admin>
    </AppContext.Provider>
  )
};

我的菜单使用这个组件的导航链接:

export const MyMenuLink = ({ primaryText, to, leftIcon, sidebarIsOpen, onMenuClick, dense, foo }) => {
    
    const clickHandler = (e) => {
        const newAppData = {
            ...appData,
            foo: foo,
        };
        setAppData(newAppData);    
    };

    return (
        <Link to={to} onClick={clickHandler}>                            
            <Typography variant="inherit">{primaryText}</Typography>            
        </Link>
    );
};

因此,当我点击该链接时,会出现路由历史警告,但如果我在clickHandler中删除setAppData()调用,警告就会消失。

英文:

I'm using context to store a global piece of information which is updated when navigating around the web app from a Link component. For some reason, when I update the context data, I get a "Warning: You cannot change &lt;Router history>" in the console. My data has no history object in it, just a value and a function to update. What could cause this?

Here is my setup:

const AppContext = createContext({
    appData: {},
    setAppData: () =&gt; {},
});

const App = () =&gt; {

  const [appData, setAppData] = useState({
    foo: null,
  });

  return (
    &lt;AppContext.Provider value={[appData, setAppData]}&gt;
        &lt;Admin title=&quot;My Admin App&quot;&gt;
          &lt;Resource name=&quot;foo&quot; show={ShowFoo} /&gt;
        &lt;/Admin&gt;
    &lt;/AppContext.Provider&gt;
  )
};

My menu has nav links using this component:

export const MyMenuLink = ({ primaryText, to, leftIcon, sidebarIsOpen, onMenuClick, dense, foo }) =&gt; {
    
    const clickHandler = (e) =&gt; {
        const newAppData = {
            ...appData,
            foo: foo,
        };
        setAppData(newAppData);    
    };

    return (
        &lt;Link to={to} onClick={clickHandler}&gt;                            
            &lt;Typography variant=&quot;inherit&quot;&gt;{primaryText}&lt;/Typography&gt;            
        &lt;/Link&gt;
    );
};

So when I click on that link, I get the router history warning, but if I remove the setAppData() call in clickHandler, it disappears.

Thanks

答案1

得分: 1

警告:您不能更改 &lt;Router history&gt;

这是来自React Router的消息。React Router支持不同的history对象,但通常您会连接到BrowserHistory并允许浏览器处理和传播路由更改给路由器。
按下浏览器的前进/后退按钮将触发重新渲染,而不是完全的页面刷新。

React-admin

React-Admin也使用了React-Router。
您可以将一个history传递给根&lt;Admin /&gt;组件。
如果您不设置history,react-admin会为您创建一个。

传递一个history很简单:

import { createBrowserHistory } from "history";

const history = createBrowserHistory();

const App = () => {
  return (<Admin
            dashboard={Dashboard}
            authProvider={authProvider}
            dataProvider={dataProvider}
            history={history}
          />);
}

重新渲染

React-Router不希望history引用发生变化。
如果您尝试更改它,您将看到警告弹出。
当父组件中的某些内容发生更改时,React将重新渲染子组件。
在您的情况下,当在提供程序中接收到新值时,上下文将重新渲染其子组件。(基本上是在调用setAppData时)
这将触发&lt;Admin&gt;组件的重新渲染。
由于我们没有传递history,会创建一个新的history并传递给路由器。可能在这里
然后您将看到React Router的警告。

传入您自己的路由器将解决这个问题。

沙箱

您可以在此沙箱中尝试它。
如果注释掉history属性,您将再次收到警告。
仪表板上有一个链接可以触发重新渲染。

英文:

Warning: You cannot change &lt;Router history&gt;

This is a message coming from React Router. React router supports different history-objects, but usually you would connect to a BrowserHistory and allow the browsers to handle and propagate route changes to the router.
IE pressing the browsers forward/backward buttons will trigger a re-render, not a total page refresh.

React-admin

React-router is also used by React-Admin.
You can pass along a history into the root &lt;Admin /&gt; component.
If you don't set a history, react-admin will create one for you.

Passing in a history is as simple as:

import {createBrowserHistory} from &quot;history&quot;;

const history = createBrowserHistory();

const App = ()=&gt;{
return (&lt;Admin
        dashboard={Dashboard}
        authProvider={authProvider}
        dataProvider={dataProvider}
        history={history}
      /&gt;)
}

Rerenders

React-router doesn't want the history reference to change.
If you attempt to change it, you'll see the warning pop up.
React will re-render child components when something in the parent changes.
In your case, the context will re-render their children whenever a new value is received in the provider. (basically when calling setAppData)
This will trigger a rerender on your &lt;Admin&gt; component.
Since we didn't pass in a history, a new one is created and passed to the router. probably here
You will then see the warning from react router.

Passing in your own router would solve that.

sandbox

You can try it in this sandbox.
If you comment out the the history prop, you will receive warnings again.
There's a link on the dashboard to trigger the re-render.

huangapple
  • 本文由 发表于 2023年6月16日 02:58:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/76484736.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定