【问题标题】:React: how to change the state if something outside the react app changes?React:如果反应应用程序之外的东西发生变化,如何改变状态?
【发布时间】:2021-02-18 03:56:47
【问题描述】:

备注:这不是Change the state when clicking outside a component in React的重复 我不想处理 mousedown/mouseup 事件。

我想“从外部”更改组件的状态。因此,组件将等到 HTML 代码执行一些操作 id(按钮单击)。 单击可以更改 DOM 中的某些内容,但是有没有办法(另一种方法是监视 buttondown 事件)来检测更改?

结果会是这样的:

<div id="root">
... the component will be rendered here, hidden at the beginning
</div>

<button onClick=....>Show the Component</button>

【问题讨论】:

  • 按钮是否是 React 组件的一部分(即使它与您想要执行此操作的位置完全不同的 UI 部分?可以吗?
  • 你所说的“从外部”是什么意思?在什么之外?
  • 不,按钮在 React 组件之外。我正在尝试将组件添加到现有的 HTML/js 页面。
  • 我认为你最好能设置一个异步任务来轮询你的状态变化。例如,让您保持登录状态的应用程序通常会对授权服务的计时器进行“刷新”调用,以保持您的登录会话处于活动状态。对用户来说,只要浏览器打开,他们的会话就会保持活动状态。如果您用“HTML 代码执行的某些操作 id”来描述您的意思,如果您不是真的指“按钮单击”,您会得到更好的答案。否则请参阅重复链接。
  • “外部”是指“在 React 组件之外”。

标签: javascript reactjs


【解决方案1】:

您至少有两个选择:

  1. 使用portal,将按钮移动到 React 树中,即使它位于 DOM 的其他位置,也可以在其他位置呈现您想要的组件。但如果你想将按钮完全保留在 React 之外,则此选项不适用。

  2. 如果您想将 button 完全留在 React 之外,请在您要渲染/不渲染的组件周围使用父组件,并让该父组件监听按钮元素的点击。在componentDidMountuseEffect 没有依赖关系的钩子中连接该点击处理程序,并在componentWillUnmount 或从useEffect 返回的清理回调中删除它。

  3. 或者从另一边驱动它,使用非React代码钩住点击,然后通过ReactDOM.render将组件直接安装在id="root"上来响应点击。

这里是#2的一个例子:

const {useState, useEffect} = React;

const TheComponent = () => {
    return <div>This is the component</div>;
};

const Example = () => {
    const [showing, setShowing] = useState(false);
    useEffect(
        () => {
            const clickHandler = () => {
                setShowing(flag => !flag);
            };
            console.log("Hooking click on button");
            document.getElementById("the-button").addEventListener("click", clickHandler);
            // return cleanup handler:
            return () => {
                console.log("Unhooking click on button");
                document.getElementById("the-button").removeEventListener("click", clickHandler);
            };
        },
        [] // <== No deps = mount/unmount only
    );
    return (
        <div>
            {showing && <TheComponent />}
        </div>
    );
};

const App = () => {
    const [mounted, setMounted] = useState(true);
    return (
        <div>
            {mounted && <Example />}
            <div>
                <input type="button" value="Unmount the parent component entirely" onClick={() => setMounted(false)} />
            </div>
        </div>
    );
};

ReactDOM.render(<App />, document.getElementById("root"));
<div>React <code>id="root"</code>:</div>
<div id="root"></div>

<div style="margin-top: 10px">Outside of React:</div>
<input type="button" id="the-button" value="Toggle">

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js"></script>

这里是#3的一个例子:

const {useState, useEffect} = React;

const TheComponent = () => {
    return <div>This is the component</div>;
};

const root = document.getElementById("root");
let mounted = false;
document.getElementById("the-button").addEventListener("click", () => {
    if (mounted) {
        ReactDOM.unmountComponentAtNode(root);
    } else {
        ReactDOM.render(<TheComponent />, root);
    }
    mounted = !mounted;
});
<div>React <code>id="root"</code>:</div>
<div id="root"></div>

<div style="margin-top: 10px">Outside of React:</div>
<input type="button" id="the-button" value="Toggle">

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js"></script>

【讨论】:

  • 我喜欢动态安装组件的想法!但这不适用于这里。
  • 您能否详细说明选项 2?如何监听“父组件”之外的元素的点击?
  • @Pepe - 通过在 DOM 中查找元素并向其添加点击处理程序。我添加了#2 和#3 的示例。 #2 的示例可能更简单,但我想演示卸载父组件,以便您可以看到事件处理程序清理发生,因为这很重要。
  • 选项 2 我正是我想要的 :) 现在,当你回答它时,这似乎很明显,但我并没有想到将“plain js”包含到 React 中。谢谢!
  • @Pepe - 我的荣幸。 :-) 如果避免它是合理的,这是你想要避免的事情,但对于那些不是的时候它绝对没问题。 :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-25
  • 1970-01-01
  • 1970-01-01
  • 2020-07-09
  • 1970-01-01
  • 2021-06-06
相关资源
最近更新 更多