我正在学习React,正在为我正在学习的课程做一个项目。我创建了一个React应用程序,它有许多功能组件,并使用React路由。
我在这里尝试实现的是,在用户完成登录过程后重新呈现NavBar组件,以便我可以基于当前上下文值以编程方式显示不同的Navbar项。
当userContext更新时,我可以看到其他组件的更新,但不能使用导航栏。我错过了什么/做错了什么?
index.js
import 'bootstrap/dist/css/bootstrap.css';
import React from 'react';
import ReactDOM from 'react-dom/client';
import reportWebVitals from './reportWebVitals';
import App from './App';
const UserContext = React.createContext(null);
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
reportWebVitals();
export default UserContext;
App.js
import 'bootstrap/dist/css/bootstrap.css';
import React from 'react';
import {HashRouter, Route, Routes} from 'react-router-dom';
import NavBar from './navbar';
import Home from './home';
import CreateAccount from './createaccount';
import Login from './login';
import Deposit from './deposit';
import Withdraw from './withdraw';
import Balance from './balance';
import AllData from './alldata';
import UserContext from './index.js'
export default function App() {
return(
<HashRouter>
<UserContext.Provider value={{users: [{name:'defaultuser', email: 'default@default.com', password: 'DFS)(Uqadfasfda234##', balance: 0, loggedIn: 0}]}}>
<NavBar/>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/CreateAccount/" element={<CreateAccount />} />
<Route path="/login/" element={<Login />} />
<Route path="/deposit/" element={<Deposit />} />
<Route path="/withdraw/" element={<Withdraw />} />
<Route path="/balance/" element={<Balance />} />
<Route path="/alldata/" element={<AllData />} />
</Routes>
</UserContext.Provider>
</HashRouter>
);
}
createaccount.js
import React from 'react';
import Card from './card.js';
import UserContext from './index.js';
import './styles.css';
import { useNavigate } from 'react-router-dom';
export default function CreateAccount(){
const [show, setShow] = React.useState(true);
const [status, setStatus] = React.useState('');
const [name, setName] = React.useState('');
const [balance, setBalance] = React.useState('');
const [email, setEmail] = React.useState('');
const [password, setPassword] = React.useState('');
const [loggedIn, setLoggedIn] = React.useState(0);
const ctx = React.useContext(UserContext);
const navigate = useNavigate();
function handleClick(){
navigate("#/login/");
}
function validate(field, label){
if (!field) {
setStatus('Error: ' + label);
setTimeout(() => setStatus(''), 3000);
return false;
}
return true;
}
function handleCreate(){
if (!validate(name, 'name')) return;
if (!validate(email, 'email')) return;
if (!validate(password, 'password')) return;
if (!validate(balance, 'balance')) return;
ctx.users.push({name,email,password,balance, loggedIn});
setShow(false);
}
function clearForm(){
setName('');
setEmail('');
setPassword('');
setBalance(0.00);
setShow(true);
}
return (
<div style={{display: "flex", alignItems:"center", justifyContent:"center"}}>
<Card
bgcolor="primary"
header="Create Account"
status={status}
body={show ? (
<>
Name<br/>
<input type="input" className="form-control" id="name" placeholder = "Enter name" value={name} onChange={e => setName(e.currentTarget.value)} /><br/>
Email address<br/>
<input type= "input" className="form-control" id="email" placeholder="Enter email" value={email} onChange={e => setEmail(e.currentTarget.value)} /><br/>
Password<br/>
<input type= "password" className ="form-control" id="password" placeholder="Enter password" value={password} onChange={e => setPassword(e.currentTarget.value)} /><br/>
Starting Balance<br/>
<input type="input" className="form-control" id="startingbalance" placeholder = "Enter starting balance" value={balance} onChange={e => setBalance(e.currentTarget.value)} /><br/>
<button type="submit" className="btn btn-light" onClick={handleCreate}>Create Account</button>
</>
):(
<>
<h5>Success</h5>
<button type="submit" className="btn btn-light" onClick={clearForm}>Add another account</button>
<div className="divider"/>
<a href="#/login/"><button type="submit" className="btn btn-light" onClick={handleClick}>Login</button></a>
</>
)}
/>
</div>
)
}
login.js
import React from 'react';
import Card from './card.js';
import UserContext from './index.js';
import DynamicTable from "./DynamicTable";
let startingBalance = 0.00;
export default function Login(){
const [loggedIn, setLoggedIn] = React.useState(0);
const [userName, setUserName] = React.useState('');
const [userPassword, setUserPassword] = React.useState('');
const [loggedinUser, setLoggedInUser] = React.useState(null);
const [user, setUser] = React.useState();
const [users, setUsers] = React.useState([]);
const ctx = React.useContext(UserContext);
function handleLogin(){
const activeLogin = ctx.users.findIndex(x => x.loggedIn === 1)
if (activeLogin === -1){
console.log('Login.js - no active login');
}
else {
if (window.confirm("There is already a logged in user session. Do you wish to log in as a different user?")) {
alert("The logged in user has changed.");
logOut();
} else {
return;
}
}
findUser(userName, userPassword);
}
function logOut(){
let updatedValue = [...ctx.users];
updatedValue.forEach(element => element.loggedIn = 0);
setUsers(updatedValue);
}
function handleUpdate(i){
let updatedValue = [...ctx.users];
updatedValue[i].loggedIn = 1;
setUsers(updatedValue);
}
function findUser(userName){
setUsers(ctx.users);
const foundidx = ctx.users.findIndex(x => x.email === userName);
const found = ctx.users[foundidx];
let userFirstName = null;
if (!found)
{
alert('No user found which matches the email entered. Please try again.');
}
else {
validatePassword(found.password, userPassword, foundidx);
userFirstName = found.name;
setUser(userFirstName);
handleUpdate(foundidx);
startingBalance = parseFloat(found.balance).toFixed(2);
found.balance = startingBalance;
ctx.users[foundidx].balance = startingBalance;
}
}
function validatePassword(password, userPassword, foundidx){
if (password === userPassword)
{
setLoggedIn(1);
setLoggedInUser(foundidx);
}
else {
alert('You have entered an incorrect password. Repeated failed attempts will result in an account lockout.');
}
}
return (
<div>
<Card
bgcolor="warning"
header={!loggedIn ? (
"Login"
):(
"Welcome Back " + user + "!"
)}
txtcolor="black"
body={!loggedIn ? (
<>
Email<br/>
<input type= "input" className="form-control" id="email" placeholder="Enter email" value={userName} onChange={e => setUserName(e.currentTarget.value)} /><br/>
Password<br/>
<input type= "password" className ="form-control" id="password" placeholder="Enter password" value={userPassword} onChange={e => setUserPassword(e.currentTarget.value)} /><br/>
<button type="submit" className="btn btn-light" onClick={handleLogin}>Login</button>
</>
):(
<>
<h6><b>Current balance:</b> ${startingBalance}</h6>
<h6>What would you like to do?</h6>
<a className="nav-link" href="#/deposit/">Make Deposit</a>
<a className="nav-link" href="#/withdraw/">Make Withdrawal</a>
</>
)}
/>
<DynamicTable />
</div>
)
}
navbar.js
import Nav from 'react-bootstrap/Nav';
import Navbar from 'react-bootstrap/Navbar';
import React from 'react';
import { OverlayTrigger, Tooltip} from 'react-bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';
import UserContext from './index.js';
export default function NavBar(){
const ctx = React.useContext(UserContext);
const activeLogin = ctx.users.findIndex(x => x.loggedIn === 1);
if (activeLogin >=0){
console.log('Navbar.js - there is a logged in user');
}
else {
console.log('Navbar.js - there is NOT a logged in user');
}
return (
<Navbar collapseOnSelect expand="lg" bg="dark" variant="dark">
<OverlayTrigger placement={'right'} overlay={<Tooltip>Show the Home Page</Tooltip>}>
<Navbar.Brand href="/#">FNBRB</Navbar.Brand>
</OverlayTrigger>
<Navbar.Toggle aria-controls="respsonive-navbar-nav" />
<Navbar.Collapse id="responsive-navbar-nav">
<Nav className="me-auto">
<OverlayTrigger placement={'right'} overlay={<Tooltip>Create a new account</Tooltip>}>
<Nav.Link href="#/CreateAccount/">Create Account</Nav.Link>
</OverlayTrigger>
<OverlayTrigger placement={'right'} overlay={<Tooltip>Log into an account</Tooltip>}>
<Nav.Link href="#/login/">Log In</Nav.Link>
</OverlayTrigger>
<OverlayTrigger placement={'right'} overlay={<Tooltip>Deposit funds</Tooltip>}>
<Nav.Link href="#/deposit/">Deposit</Nav.Link>
</OverlayTrigger>
<OverlayTrigger placement={'right'} overlay={<Tooltip>Withdraw funds</Tooltip>}>
<Nav.Link href="#/withdraw/">Withdraw</Nav.Link>
</OverlayTrigger>
<OverlayTrigger placement={'right'} overlay={<Tooltip>Show all user accounts</Tooltip>}>
<Nav.Link href="#/alldata/">All Data</Nav.Link>
</OverlayTrigger>
</Nav>
</Navbar.Collapse>
</Navbar>
);
}
DynamicTable.js
import React from 'react';
import UserContext from './index.js';
function myFunction(item, index, arr){
arr[index]['key'] = index;
}
function DynamicTable(){
// get table column
const ctx = React.useContext(UserContext);
const userData = ctx.users;
userData.forEach(myFunction);
const column = Object.keys(userData[0]);
// get table heading data
const ThData =()=>{
return column.map((data)=>{
return <th key={data}>{data}</th>
})
}
// get table row data
const tdData =() =>{
return userData.map((data)=>{
return(
<tr>
{
column.map((v)=>{
return <td>{data[v]}</td>
})
}
</tr>
)
})
}
return (
<table className="table">
<thead>
<tr>{ThData()}</tr>
</thead>
<tbody>
{tdData()}
</tbody>
</table>
)
}
export default DynamicTable;
card.js
import React from 'react';
function Card(props){
function classes(){
const bg = props.bgcolor ? ' bg-' + props.bgcolor : ' ';
const txt = props.txtcolor ? ' text-' + props.txtcolor: ' text-white';
return 'card mb-3' + bg + txt;
}
return(
<div className={classes()} style={{maxWidth: "18rem"}}>
<div className="card-header">{props.header}</div>
<div className="card-body">
{props.title && (<h5 className="card-title">{props.title}</h5>)}
{props.text && (<p className="card-text">{props.text}</p>)}
{props.body}
{props.status && (<div id='createStatus'>{props.status}</div>)}
</div>
</div>
);
}
export default Card;
1条答案
按热度按时间5rgfhyps1#
不要改变状态...或其他任何内容
你的组件不会重新呈现,因为你实际上并没有改变上下文的值,也就是说,上下文的值仍然是对同一个对象的引用。
您所做的就是使用以下代码行 mutate 上下文的值:
ctx
仍然是相同的对象,ctx.users
仍然是相同的数组,但内容已更改。此类更改不会触发React重新呈现。若要更改上下文的值,必须用新对象替换整个对象。您需要:
1.将上下文的值存储在
App
中的useState
挂钩中。1.为内部组件调用
setState
提供一些方法。大致如下: