简体   繁体   English

如何将 prop 从一个组件传递到另一个组件

[英]How to pass prop from one component to another component

I am trying to pass the name, bio, experience, ctc props from InputForUserProfile to the userprofile.So that I can display them in my userprofile when ever any changes happen.我正在尝试将名称、简历、经验、ctc 道具从 InputForUserProfile 传递到用户配置文件。这样我就可以在发生任何更改时将它们显示在我的用户配置文件中。 But I can't pass them and itsays undefined when I logged them in console.Note(userprofile.js is a custom web component I created)These are the codes I tried.但是我无法传递它们,当我将它们登录到控制台时它说未定义。注意(userprofile.js 是我创建的自定义 web 组件)这些是我尝试过的代码。

App.js:应用程序.js:

import "./App.css";
import Routing from "./Routing";
function App() {
  return (
    <div className="App">
      <Routing/>
    </div>
  );
}
export default App;

Routing.js:路由.js:

import InputForUserProfile from "./InputForUserProfile";
import "./userprofile.js";
import { Link, Route, Routes } from "react-router-dom";
const Routing = ({name, bio, experience, ctc}) => {
    console.log("1", name);
  return (
    <>
      <nav>
        <ul>
          <li>
            <Link to="*">InputFields</Link>
          </li>
          <li>
            <Link to="/userprofile">userprofile</Link>
          </li>
        </ul>
      </nav>
      <Routes>
        <Route path="*" element={<InputForUserProfile />} />
        <Route
          path="/userprofile"
          element={
            <user-profile
              name={name}
              bio={bio}
              exp={experience}
              ctc={ctc}
            />
          }
        />
      </Routes>
    </>
  );
};
export default Routing;

InputForUserProfile.js: InputForUserProfile.js:

import { useState } from "react";
import { Route, Routes } from "react-router-dom";
import "./userprofile.js";
const InputForUserProfile = () => {
  const [name, setName] = useState(localStorage.getItem("name") || "");
  const [bio, setBio] = useState(localStorage.getItem("bio") || "");
  const [experience, setExperience] = useState(
    localStorage.getItem("experience") || ""
  );
  const [ctc, setCtc] = useState(localStorage.getItem("ctc") || "");

  const handleSubmit = (event) => {
    event.preventDefault();
    localStorage.setItem("name", name);
    localStorage.setItem("bio", bio);
    localStorage.setItem("experience", experience);
    localStorage.setItem("ctc", ctc);
  };
  return (
    <>
      <form onSubmit={handleSubmit}>
        <label>
          Change User Name:
          <input
            type="text"
            placeholder="Change User Name"
            value={name}
            onChange={(e) => setName(e.target.value)}
          />
        </label>
        <br />
        <label>
          Change User Bio:
          <textarea
            placeholder="Change User Bio"
            value={bio}
            onChange={(e) => setBio(e.target.value)}
          />
        </label>
        <br />
        <label>
          Change User Experience:
          <input
            type="text"
            placeholder="Change User Experience"
            value={experience}
            onChange={(e) => setExperience(e.target.value)}
          />
        </label>
        <br />
        <label>
          Change User CTC:
          <input
            type="text"
            placeholder="Change User CTC"
            value={ctc}
            onChange={(e) => setCtc(e.target.value)}
          />
        </label>
        <br />
        <button type="submit">Save Changes</button>
      </form>
      <Routes>
        <Route
          path="/userprofile"
          element={
            <user-profile name={name} bio={bio} exp={experience} ctc={ctc} />
          }
        />
      </Routes>
    </>
  );
};
export default InputForUserProfile;

userprofile.js(custom web component): userprofile.js(自定义 web 组件):

class UserProfile extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: "open" });
    this.shadowRoot.innerHTML = `
        <div id="profile">
        <br /><br />
        <img
          src=""
          alt="Profile Picture"
         
        />
        <h1>
          Name:
          <p id="name"></p>
        </h1>
    
        <h1>
          BIO:
          <p id="bio"></p>
        </h1>
        <h1>
          Experiance:
          <p id="exp"></p>
        </h1>
        <h1>
          CTC:
          <p id="CTC"></p>
        </h1>
        <input type="text" id="user-name"  class="hide-input" placeholder="changeusername">
        <input type="text" id="user-bio" class="hide-input" placeholder="changeuserbio">
        <input type="text" id="user-experience" class="hide-input" placeholder="changeuserexperience">
        <input type="text" id="user-CTC" class="hide-input" placeholder="changeuserCTC">
        <button id="save-button" class="hide-input" >save</button>
    
    
        <button id="edit-button"  >Edit Profile</button
        ><br /><br />
      </div>`;
  }

  connectedCallback() {
    const userVaule = this.shadowRoot.querySelector("div");
    this.shadowRoot.querySelector('#name').textContent = this.getAttribute("name");
    this.shadowRoot.querySelector('#bio').textContent = this.getAttribute("bio");
    this.shadowRoot.querySelector('#exp').textContent = this.getAttribute("exp");
    this.shadowRoot.querySelector('#CTC').textContent = this.getAttribute("ctc");
    userVaule
      .querySelector("#save-button")
      .addEventListener("click", this.saveProfile.bind(this));
    userVaule
      .querySelector("#edit-button")
      .addEventListener("click", this.editProfile.bind(this));
    userVaule.querySelectorAll("input, #save-button").forEach((el) => {
      el.classList.add("hide-input");
    });
    
  }
 editProfile() {
    this.shadowRoot.querySelectorAll("input, #save-button").forEach((el) => {
      el.classList.remove("hide-input");
    });

    this.shadowRoot.querySelector("#user-name").value =
      localStorage.getItem("name") || "";

    this.shadowRoot.querySelector("#user-bio").value =
      localStorage.getItem("bio") || "";

    this.shadowRoot.querySelector("#user-experience").value =
      localStorage.getItem("experience") || "";

    this.shadowRoot.querySelector("#user-CTC").value =
      localStorage.getItem("ctc") || "";

  }
  saveProfile() {
    this.shadowRoot.querySelectorAll("input, #save-button").forEach((el) => {
      el.classList.add("hide-input");
    });

    let name = this.shadowRoot.querySelector("#name");
    let bio = this.shadowRoot.querySelector("#bio");
    let exp = this.shadowRoot.querySelector("#exp");
    let CTC = this.shadowRoot.querySelector("#CTC");

    const userName = this.shadowRoot.querySelector("#user-name").value;
    localStorage.setItem("name", userName);

    const userBio = this.shadowRoot.querySelector("#user-bio").value;
    localStorage.setItem("bio", userBio);

    const userExperience =
      this.shadowRoot.querySelector("#user-experience").value;
    localStorage.setItem("exp", userExperience);

    const userCTC = this.shadowRoot.querySelector("#user-CTC").value;
    localStorage.setItem("CTC", userCTC);

    name.textContent = userName;
    bio.textContent = userBio;
    exp.textContent = userExperience;
    CTC.textContent = userCTC;

    
  }
}

customElements.define("user-profile", UserProfile);

There are two problems with your code - one for now and another in future.您的代码有两个问题 - 一个是现在,另一个是将来。

First is properties vs attributes .首先是properties 与 attributes React doesn't support passing props (non-primitive types like objects, array, etc.) to the custom elements. React 不支持将道具(非原始类型,如对象、数组等)传递给自定义元素。 Only values like strings, numbers are supported and they are passed as attributes instead of DOM properties.仅支持字符串、数字等值,它们作为属性而不是 DOM 属性传递。 (But this doesn't look to be the issue right now as all the data is just strings). (但这现在看起来不是问题,因为所有数据都只是字符串)。

The way to get around is to wrap your web component in some react component and use the hook useRef to get instance of actual web component and when the props changes, you change the prop of the web component like:绕过的方法是将 web 组件包装在一些反应组件中,并使用钩子useRef获取实际 web 组件的实例,当道具发生变化时,您更改 web 组件的道具,如:

function MyWrapperComp(props) {

  const ref = useRef(null);

  // Observe
  useEffect(() => {
    // Set the name as prop as opposed to attribute
    ref.current?.name = props.name;
  }, props.name);

  return (
    <user-profile ref={ref}></user-profile>
  );
}

If you are fine with using just attribute (as opposed to props), then the second issue is the design of your web component.如果您不介意只使用属性(而不是 props),那么第二个问题是 web 组件的设计。 It may happen that your component is initialized but react has not yet passed the props to the component (async behavior or something similar - maybe localstorage empty initially).可能会发生您的组件已初始化但 React 尚未将 props 传递给组件(异步行为或类似行为 - 可能localstorage最初为空)。 To get around that, you should listen for attributeChangedCallback lifecycle event or use MutationObserver to observe attribute changes done by React.为了解决这个问题,您应该监听attributeChangedCallback生命周期事件或使用MutationObserver来观察 React 所做的属性更改。 For example:例如:

class UserProfile extends HTMLElement {

  // Rest of the code.

  attributeChangedCallback(name, oldValue, newValue) {
    if (name === 'name' && oldValue !== newValue) {
      // Write code here when attribute `name` is changed.
    }
  }
}

Finally to answer your original question - How to pass prop from one component to another component - unless you are using any declaration abstraction on top of web components, the only way is to grab the instance of the component and pass props to it using that instance.最后回答你原来的问题 -如何将 prop 从一个组件传递到另一个组件- 除非你在 web 组件之上使用任何声明抽象,否则唯一的方法是获取组件的实例并使用该实例将 props 传递给它.

On a side note, writing web components without any abstraction is cumbersome and extremely error prone.附带说明一下,在没有任何抽象的情况下编写 web 组件很麻烦,而且极易出错。 I would recommend something like lit-element to author web components it provide many utilities to ease the pain.我会推荐像lit-element这样的东西来编写 web 组件,它提供了许多实用程序来减轻痛苦。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM