React Portal输入更改删除元素

问题描述 投票:1回答:1

我已经创建了这支笔来演示它:https://codepen.io/no1melman/pen/WWyJqQ

基本上我有这个门户网站:

const ChatArea = ({ children }) => {
  const el = document.createElement("div");
  el.classList.add('chatbox')
  const root = document.getElementById("rootus");

  useEffect(() => {
    root.appendChild(el);
    return () => root.removeChild(el);
  }, []);

  return createPortal(children, el);
};

并使用像:

const ChatBox = () => {
  const [ reply, setReply ] = useState('');
  const handleReply = (e) => {
    e.preventDefault();
    setReply(e.target.value);
  }
  return (
    <ChatArea>
      <div className="chat-title">Bot Convo</div>

      <div className="chat-convo"></div>

      <div className="chat-reply">
        <input type="text" value={reply} onChange={handleReply} />
        <button>Send</button>
      </div>
    </ChatArea>
  )
}

出于某种原因,当你开始输入聊天框的主体似乎消失了...我已经把日志放入useEffect,看看是否是它造成的,但它没有显示我的想法

javascript reactjs react-hooks
1个回答
2
投票

这里有两个问题:

第一个问题是

useEffect(() => {
    root.appendChild(el);
    return () => root.removeChild(el);
  }, []);

现在根据钩子原则,依赖项应该与钩子内使用的变量匹配。如果没有使用,反应将不会在下次运行效果。

因此,当您点击打开聊天时,它会打开聊天框。效果运行并使用输入框呈现门户。

当你键入第一个字母并且onChange发生时

它触发了ChatArea的重新渲染,理想情况下应该再次运行效果,但是没有运行,因为依赖数组是空白的,并且反应不知道何时重新渲染。因此,第一次运行chatArea时效果运行了一次安装了UI,下次,由于依赖项数组为空,效果未运行。

这一行:

return createPortal(children,el); //指的是创建但未附加到DOM的新el。因此,在聊天框内的UI上看不到任何内容。

请参考此链接:do not miss dependencies React hooks常见问题解答部分很棒:)

第二期:

理想情况下,不应每次都创建新的div。在连续的重新渲染中保持“div”元素

看到这个实现:我知道可以有其他方法来实现它。欢迎提供反馈。

const {
  render,
  createPortal
} = ReactDOM;
const {
  useState,
  useEffect,
  useRef
} = React;

const ChatArea = ({
  children
}) => {
  const el = document.createElement("div");
  el.classList.add('chatbox')
  // This el above will be different in each render 

  // root will remain same, ideally root and chatdiv should be passed as props
  const root = document.getElementById("rootus");

  // this val and setVal is done to toggle render the chart area after
  // chatDiv is updated
  const [val, setVal] = useState(true)


  const chatDiv = useRef(null)

  // First useEffect to persist the div 
  useEffect(() => {
    if (!chatDiv.current) {
      chatDiv.current = el
      setVal(!val)
    }
  }, [chatDiv])

  useEffect(() => {
    root.appendChild(chatDiv.current);
    return () => {
      return root.removeChild(chatDiv.current)
    }; // you are removing it 
  }, [chatDiv, root]);

  if (chatDiv.current) {
    return createPortal(children, chatDiv.current)
  }
  return null
  // In your case as the return happened first and found out the el

};

const ChatBox = () => {
  const [reply, setReply] = useState('');
  const handleReply = (e) => {
    e.preventDefault();
    setReply(e.target.value);
  }
  return ( <
    ChatArea >
    <
    div className = "chat-title" > Bot Convo < /div>

    <
    div className = "chat-convo" > < /div>

    <
    div className = "chat-reply" >
    <
    input type = "text"
    value = {
      reply
    }
    onChange = {
      handleReply
    }
    /> <
    button > Send < /button> <
    /div> <
    /ChatArea>
  )
}

const NavBar = ({}) => ( <
  div className = "navbar" >
  <
  div > Home < /div> <
  div > Somewhere < /div> <
  /div>
);
const Main = () => {
  const [showChat, setShowChat] = useState(false);
  const openChat = () => {
    setShowChat(true);
  };
  const chatterbox = showChat ? ( < ChatBox / > ) : null;

  return ( <
    div className = "container" >
    <
    h2 > Main < /h2> <
    p >
    It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.The point of
    using Lorem Ipsum is that it has a more - or - less normal distribution of
    letters, as opposed to using 'Content here, content here', making it look like readable English.Many desktop publishing packages and web page editors now use Lorem Ipsum as their
    default model text, and a search
    for 'lorem ipsum'
    will uncover many web sites still in their infancy.Various versions have evolved over the years, sometimes by accident, sometimes on purpose(injected humour and the like). <
    /p> <
    p style = {
      {
        display: "flex",
        justifyContent: "center"
      }
    } >
    <
    button onClick = {
      openChat
    } > Open Chat < /button> <
    /p> <
    p style = {
      {
        display: "flex",
        flexDirection: "column",
        justifyContent: "center",
        backgroundColor: "red"
      }
    } >
    {
      chatterbox
    } < /p> <
    /div>
  );
};
const App = ({}) => ( <
  div className = "app" >
  <
  NavBar / >
  <
  Main / >
  <
  /div>
);

render( < App / > , document.getElementById("rootus"));
body {
  font-family: Raleway;
}

* {
  box-sizing: border-box;
}

#rootus {
  position: relative;
  height: 100vh;
  display: flex;
  justify-content: center;
}

.navbar {
  display: flex;
  justify-content: center;
}

.navbar>div {
  padding: 10px;
}

.navbar>div:hover {
  background-color: gray;
  cursor: pointer;
}

.container {
  width: 960px;
}

.app {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.chatbox {
  width: 400px;
  height: 200px;
  position: absolute;
  bottom: 0;
  border: 2px solid black;
  background: white;
  display: flex;
  flex-direction: column;
}

.chat-title {
  background: black;
  color: white;
}

.chat-convo {
  flex: 1;
  display: flex;
}

.chat-reply {
  display: flex;
  border-top: 1px solid black;
}

.chat-reply>input {
  width: 80%;
  padding: 8px;
  border: none;
  outline: none;
}

.chat-reply>button {
  outline: none;
  border: none;
  flex: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>

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

</div>

Ui没有在stackoverflow代码片段中出现,所以我不得不在样式中编辑一些东西。您可以根据您的原始样式查看代码笔codepen linkac

© www.soinside.com 2019 - 2024. All rights reserved.