简体   繁体   中英

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. 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.

App.js:

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

Routing.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:

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):

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 . React doesn't support passing props (non-primitive types like objects, array, etc.) to the custom elements. Only values like strings, numbers are supported and they are passed as attributes instead of DOM properties. (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:

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. 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). To get around that, you should listen for attributeChangedCallback lifecycle event or use MutationObserver to observe attribute changes done by 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.

On a side note, writing web components without any abstraction is cumbersome and extremely error prone. I would recommend something like lit-element to author web components it provide many utilities to ease the pain.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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