簡體   English   中英

在反應鈎子之間傳遞數據

[英]Pass data between react hooks

如何將數據從一個React hooks表單(組件)傳遞到另一個組件。 例如,如果我需要Profile.js中的玩家name and photo通行證並使其在Navigation.js中可用,我該怎么做?

Player.js

import React, { useContext , useEffect, useState } from "react";
import { useForm } from 'react-hook-form';
import { useHistory } from "react-router-dom";
import Axios from "axios";
import UserProfileContext from '../context';

const Profile = () => {

  const { setData } = useContext(UserProfileContext);
  const [email, setEmail] = useState('');
  const [picture, setPicture] = useState('');
  const [playerProfile, setPlayerProfile] = useState([]);
  const loginUserEmail = localStorage.getItem('loginEmail');
  const [updateProfile, setUpdateProfile] = useState({ _id: '', photo: '', name: '', email:'', phonenumber: '', position: '', password: '' })
  const [isSent, setIsSent] = useState(false);
  const [helperText, setHelperText] = useState('');
  const [disabled, setDisabled] = useState(true);
  const { handleSubmit, register, errors } = useForm();
  const history = useHistory();

  const onChangePicture = e => {
    console.log('picture: ', picture);
    if (e.target.files.length) {
      setPicture(URL.createObjectURL(e.target.files[0]));
    } else {
      return false;
    }
  };

  // If no profile image is being uploaded, to avoid the broken display of image, display a default image.
  const addDefaultSrc = e => {
    e.target.src = '/images/default-icon.png';
  }

  // Pass the id to the handler so you will know which item id changing.
  const handleChange = (e, id) => {
    e.persist();
    let itemIndex;
    const targetPlayer = playerProfile.find((player, index) => {
      console.log({ player, id, index });
      itemIndex = index; // Track the index so you can use it to update later.
      return player.id === id;
    });

    console.log({ targetPlayer, id, e });

    const editedTarget = {
      ...targetPlayer,
      [e.target.name]: e.target.value
    };
    const tempPlayers = Array.from(playerProfile);
    tempPlayers[itemIndex] = editedTarget;
    /*
    // Alternatively:: you can just  map over the array if you dont want to track the index
    const tempPlayers = playerProfile.map((profile, index) => {
      return profile.id === id ? editedTarget : profile;
    });
    */
    setPlayerProfile(tempPlayers);
    setUpdateProfile({ ...updateProfile, [e.target.name]: e.target.value }); // this is added just to see if its working
  };

  useEffect(() => {
    const fetchData = async () => {
      try {
        const params = {
          email: loginUserEmail,
        };
      const res = await Axios.get('http://localhost:8000/service/profile', {params});
        setPlayerProfile(res.data.playerProfile);
      } catch (e) {
        console.log(e);
      }
    }
    fetchData();
  }, []);

  const onSubmit = () => {
    setDisabled(disabled);
    const fetchData = async () => {
      try {
        const params = {
          email: loginUserEmail,
        };
        const data = {photo: updateProfile.photo, name: updateProfile.name, email: updateProfile.email, phonenumber: updateProfile.phonenumber, position: updateProfile.position, password: updateProfile.password}
        const res = await Axios.put('http://localhost:8000/service/profile', data, {params}); 
        console.log("Front End update message:" + res.data.success);
        if (res.data.success) {
          setIsSent(true);
          history.push('/')
        }
        else {
          console.log(res.data.message);
          setHelperText(res.data.message);
        }
      } catch (e) {
        setHelperText(e.response.data.message);
      }
    }
    fetchData();
  }

  return (
    <div className="register_wrapper">
      <div className="register_player_column_layout_one">
        <div className="register_player_Twocolumn_layout_two">
          <form onSubmit={handleSubmit(onSubmit)} className="myForm">
            {
              playerProfile.map(({ id, photo, name, email, phonenumber, position, privilege, password }) => (
                <div key={id}>
                  <div className="formInstructionsDiv formElement">
                    <h2 className="formTitle">Profile</h2>
                    <div className="register_profile_image">
                      <input id="profilePic" name="photo" type="file" onChange={onChangePicture} />
                    </div>
                    <div className="previewProfilePic" >
                      <img alt="" onError={addDefaultSrc} name="previewImage" className="playerProfilePic_home_tile" src={photo} onChange={e => handleChange(e, id)}></img>
                    </div>
                  </div>
                  <div className="fillContentDiv formElement">
                    <label>
                      <input className="inputRequest formContentElement" name="name" type="text" value={name} 
                      onChange={e => handleChange(e, id)}
                      maxLength={30}
                      ref={register({
                        required: "Full name is required", 
                        pattern: {
                          value: /^[a-zA-Z\s]{3,30}$/,
                          message: "Full name should have minimum of 3 letters"
                        }
                      })}
                      />
                      <span className="registerErrorTextFormat">{errors.name && errors.name.message}</span>
                    </label>
                    <label>
                      <input className="inputRequest formContentElement" name="email" type="text" value={email} 
                      onChange={e => handleChange(e, id)}
                      disabled={disabled}
                      />
                    </label>
                    <label>
                      <input className="inputRequest formContentElement" name="phonenumber" type="text" value={phonenumber} 
                      onChange={e => handleChange(e, id)}
                      maxLength={11}
                      ref={register({
                        required: "Phone number is required",
                        pattern: {
                          value: /^[0-9\b]+$/,
                          message: "Invalid phone number"
                        }
                       })}
                      />
                      <span className="registerErrorTextFormat">{errors.phonenumber && errors.phonenumber.message}</span>
                    </label>
                    <label>
                      <input className="inputRequest formContentElement" name="position" type="text" value={position} 
                      onChange={e => handleChange(e, id)}
                      maxLength={30}
                      ref={register({
                        pattern: {
                          value: /^[a-zA-Z\s]{2,30}$/,
                          message: "Position should have minimum of 2 letters"
                        }
                      })}
                      />
                      <span className="registerErrorTextFormat">{errors.position && errors.position.message}</span>
                    </label>
                    <label>
                      <div className="select" >
                        <select name="privilege" id="select" value={privilege} onChange={e => handleChange(e, id)}>
                          {/*<option selected disabled>Choose an option</option> */}
                          <option value="player">PLAYER</option>
                          {/*<option value="admin">ADMIN</option>*/}
                        </select>
                      </div>
                    </label>
                    <label>
                      <input className="inputRequest formContentElement" name="password" type="password" value={password} 
                      onChange={e => handleChange(e, id)}
                      minLength={4}
                      maxLength={30}
                      ref={register({
                      required: "Password is required",
                      pattern: {
                        value: /^(?=.*?\d)(?=.*?[a-zA-Z])[a-zA-Z\d]+$/,
                        message: "Password begin with a letter and includes number !"
                      }
                      })}
                      />
                      <span className="registerErrorTextFormat">{errors.password && errors.password.message}</span>
                    </label>
                  </div>
                  <label>
                    <span className="profileValidationText">{helperText}</span>
                  </label>
                  <div className="submitButtonDiv formElement">
                    <button type="submit" className="submitButton">Save</button>
                  </div>
                </div>
              ))
            }
          </form>

        </div>
      </div>
    </div>
  );
}

Navigation.js

import React, { useContext } from 'react';
import { NavLink, useHistory } from 'react-router-dom';
import UserProfileContext from '../context';


const Navigation = () => {
    const history = useHistory();
    const { data } = useContext(UserProfileContext);

    const divStyle = {
        float:'left',
        color: '#64cad8', 
        padding: '0px 0px 0px 10px',
        font:'Lucida, sans-serif'
      };

    function logout() {
        localStorage.removeItem('loginEmail')
        localStorage.removeItem('Privilege')
        history.push('/login')
        window.location.reload(true);
      }

    return localStorage.getItem('loginEmail') &&
        <div className="App">
            <div className="wrapper">
                <nav className="siteNavigation_nav_links">
                <div className="clubLogo landing"style={divStyle}><b>Southside Soccer</b></div>
                    <NavLink className="mobile_register_link" to="/">Home</NavLink>
                    <NavLink className="mobile_register_link" to="/profile">Profile</NavLink>
                    <NavLink className="mobile_login_link" to="/login" onClick={logout}>Logout</NavLink>
                    <NavLink className="mobile_login_link" to='/aboutus'>About us</NavLink>
                <div className="profileImage nav menu">
                <span>{data.name}</span>|<img src=""></img>
                </div>
                </nav>
            </div>
        </div>
}

export default Navigation;

App.js

import React, { useState } from 'react';
import "./App.css";
import "./CSSModules/home.css";
import "./CSSModules/register.css";
import "./CSSModules/login.css";
import "./CSSModules/aboutus.css";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import Home from "./components/Home";
import Register from "./components/Register";
import Login from "./components/Login";
import Aboutus from "./components/Aboutus";
import Navigation from "./components/Navigation";
import Profile from "./components/Profile";
import { ProtectedRoute } from "./components/protected.route";
import UserProfileContext from './context';

var ReactDOM = require("react-dom");

const App = () => {
  const [data, setData] = useState({
    id: '',
    name: '',
    email: '',
    photo: '',
  });

  return (
   <UserProfileContext.Provider value={{ data, setData }}>
      <BrowserRouter>
        <>
     <Navigation />
      <Switch>
          <ProtectedRoute exact path="/" component={Home} />
          <ProtectedRoute path="/profile" component={Profile} />
          <ProtectedRoute path="/aboutus" component={Aboutus} />
          <Route path="/register" component={Register} />
          <Route path="/login" component={Login} />
      </Switch>
    </>
   </BrowserRouter>
  </UserProfileContext.Provider>
  );
};
ReactDOM.render(
  React.createElement(App, null),
  document.getElementById("root")
);

export default App;

context.js

import React from 'react';

export default React.createContext();

就像評論中提到的那樣,一種選擇是將應用程序的 state 保留起來(這應該是簡單狀態的首選選項)。

在實踐中,這看起來像:

應用程序.js

import React, { useState } from 'react';

import Navigation from './Navigation';
import Profile from './Profile';

function App() {
  const [name, setName] = useState('');

  return (
    <div className="App">
      <Navigation name={name} />
      <hr />
      <Profile name={name} setName={setName} />
    </div>
  );
}

export default App;

Profile.js:

import React from 'react';

const Profile = ({ name, setName }) => {
  return (
    <>
      <div>Profile: {name}</div>
      <input
        type="text"
        name="name"
        value={name}
        onChange={e => setName(e.target.value)}
      />
    </>
  );
};

export default Profile;

導航.js:

import React from 'react';

const Navigation = ({ name }) => {
  return <div>Navigation: {name}</div>;
};

export default Navigation;

編輯:仔細查看您的代碼后,我認為在這種情況下使用上下文 API 更有意義。

嘗試以下操作:

上下文.js

import React from 'react';

export default React.createContext();

應用程序.js

import React, { useState } from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';

import './styles.css';
import Profile from './components/Profile';
import Navigation from './components/Navigation';
import UserProfileContext from './context';

const App = () => {
  const [data, setData] = useState({
    id: 'player-1',
    name: 'Player One',
    age: 25,
    photo: 'rose.JPG',
  });

  return (
    <UserProfileContext.Provider value={{ data, setData }}>
      <BrowserRouter>
        <Navigation />
        <Switch>
          <Route path="/profile" component={Profile} />
        </Switch>
      </BrowserRouter>
    </UserProfileContext.Provider>
  );
};

export default App;

組件/Navigation.js

import React, { useContext } from 'react';
import { NavLink } from 'react-router-dom';

import UserProfileContext from '../context';

const Navigation = () => {
  const { data } = useContext(UserProfileContext);

  const divStyle = {
    float: 'left',
    color: '#64cad8',
    padding: '0px 0px 0px 10px',
    font: 'Lucida, sans-serif',
  };

  return (
    <div className="App">
      <div className="wrapper">
        <nav className="siteNavigation_nav_links">
          <div className="clubLogo landing" style={divStyle}>
            <b>Soccer</b>
          </div>

          <NavLink className="mobile_register_link" to="/profile">
            Profile
          </NavLink>

          <div className="profileImage nav menu">
            <span>{data.name}</span> | <img alt="" src={data.photo} />
          </div>
        </nav>
      </div>
    </div>
  );
};

export default Navigation;

組件/Profile.js

import React, { useContext } from 'react';
import { useForm } from 'react-hook-form';

import UserProfileContext from '../context';

const Profile = () => {
  const { setData } = useContext(UserProfileContext);
  const { register, handleSubmit } = useForm();

  return (
    <div>
      <form onSubmit={handleSubmit(setData)}>
        <b>Profile</b>
        <input name="id" ref={register} />
        <input name="name" ref={register} />
        <input name="age" ref={register} />
        <button type="submit" className="submitButton">
          Click
        </button>
      </form>
    </div>
  );
};

export default Profile;

您可以使用react-redux進行全局 state 處理或將回調 function 從Navigation.js傳遞到Profile.js

使用 React 的內置上下文 API。 將您的應用程序包裝在上下文提供程序中,然后您可以使用 useContext 掛鈎訪問 state,在組件之間調度 state 更新。

應用級別設置 - 一次

// Define Context
const AppContext = React.createContext()

// Define initial state
const initialState = {}

// Define Reducer
const Reducer = (state, dispatch) => {
  switch(action.type) {
    case "ACTION_TYPE": return {...state, prop: action.payload}
    default: return state
  }
}


//Wrap main App in context, pass state from React's useReducer hook
const [state, dispatch] = useReducer(Reducer, initialState)
<AppContext.Provider data={{
  state,
  dispatch
}}>
  <ReactAppMainWrapper />
</AppContext.Provider>

組件級別

const {state, dispatch} = useContext(AppContext);

// to update state call dispatch from anywhere, all components consuming state will be updated

dispatch({
   type: "ACTION_TYPE",
   payload: "new_value"
})

解釋

state 用作 redux 之類的商店,可在整個應用程序中使用。

在任何組件中,導入 AppContext 並使用 React 的內置 useContext 鈎子與組件中的 store 進行交互。

上述上下文提供程序中傳遞的數據 object 可從此獲得。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM