单击 ReactJS 按钮后 localStorage 被重置
localStorage gets reset after button click ReactJS
我目前遇到的问题是,在尝试将属于不同页面(如下所示)的项目添加到本地存储后,本地存储被重置。本地存储功能正用于购物车功能。
当我尝试添加同一类别的项目时一切正常,但一旦我切换类别,本地存储就会被重置。
我还注意到一个奇怪的行为是,对于我尝试添加到购物车的第一个项目,我必须双击它才能在本地存储中注册。
我已经设置了程序,只有购物车页面需要访问本地存储。
正在添加“可穿戴设备类别”中的产品
返回并进入“计算机”部分中的项目。 忽略边栏
从可穿戴部分添加项目并清除本地存储。
代码:
App.js
class App extends Component {
userData;
constructor(props) {
super(props);
this.state = {
cart: [],
};
this.handleAddToCart = this.handleAddToCart.bind(this);
}
handleAddToCart = (productId, prodName, description, price) => {
console.log(" Handle Add to Cart Called ", productId);
console.log("->cart state: ", this.state.cart);
const holder = {
productId,
quantity: 1,
prodName,
description,
price,
};
const idx = this.indexOfProduct(productId);
if (idx == -1) {
// Product does not exist in cart
this.setState(
{
cart: [...this.state.cart, holder],
},
() => {
console.log("Updated Cart: ", this.state.cart);
}
);
} else {
let newArray = [...this.state.cart];
newArray[idx] = {
...newArray[idx],
quantity: newArray[idx].quantity + 1,
};
this.setState(
{
cart: newArray,
},
() => {
console.log("Updated Cart: ", this.state.cart);
}
);
}
localStorage.setItem("cart", JSON.stringify(this.state.cart));
};
indexOfProduct(productId) {
for (let index = 0; index < this.state.cart.length; index++) {
if (this.state.cart[index].productId == productId) return index;
}
return -1;
}
render() {
return (
<div className="App">
{/* <div className="container-fluid">
<NavBarComponent />
</div> */}
<>
<Router>
<div className="container-fluid">
<NavBarComponent />
</div>
<Switch>
<Route exact path="/sidebar">
<SideBarComponent />
</Route>
<Route exact path="/products/:category">
<ProductGridComponent />
</Route>
<Route exact path="/cart">
<ShoppingCartComponent />
</Route>
<Route exact path="/product/:id">
{/*onAddToCart={this.handleAddToCart} */}
<ProductViewComponent onAddToCart={this.handleAddToCart} />
</Route>
<Route exact path="/contact">
<ContactUsComponent />
</Route>
<Route exact path="/about-us">
<AboutUsComponent />
</Route>
<Route exact path="/">
<HomeComponent />
</Route>
</Switch>
</Router>
</>
<FooterComponent />
</div>
);
}
}
export default App;
ShoppingCartComponent.jsx
class ShoppingCartComponent extends Component {
constructor(props) {
super(props);
this.state = {
cart: [],
};
console.log("Hello Im the constructor");
}
static getDerivedStateFromProps(props, state) {
console.log("Hello Im the dState Func");
const sCart = localStorage.getItem("cart");
const parsedCart = JSON.parse(sCart);
if (sCart == null) {
return { cart: [] };
} else {
console.log("cart String mount on shopping cart: ", sCart);
console.log("cart Object at mount on shopping cart: ", parsedCart);
return { cart: parsedCart };
console.log("After appending", this.state.cart);
}
}
render() {
console.log("Shopping Cart Array at Render(): ", this.state.cart);
return (
<div className="container mt-5 p-3 rounded cart">
<div className="row no-gutters">
<div className="col-md-8">
<div className="product-details mr-2">
<div className="d-flex flex-row align-items-center">
<i className="fa fa-arrow"></i>
<button /* onClick={history.back} */>
<span className="ml-2">
<a style={{ color: "black" }}>Continue Shopping</a>
</span>
</button>
</div>
<hr />
<h6 className="mb-0">Shopping cart</h6>
<div className="d-flex justify-content-between">
<span>
You have {this.state.cart.length} items in your cart
</span>
<div className="d-flex flex-row align-items-center">
<span className="text-black-50">Sort by:</span>
<div className="price ml-2">
<span className="mr-1">price</span>
<i className="fa fa-angle-down"></i>
</div>
</div>
</div>
{this.state.cart.map((product) => (
<div className="d-flex justify-content-between align-items-center mt-3 p-2 items rounded">
<div className="d-flex flex-row">
<img
className="rounded"
src="https://i.imgur.com/QRwjbm5.jpg"
width="40"
/>
<div className="ml-2">
<span className="font-weight-bold d-block">
{product.prodName}
</span>
<span className="spec">256GB, Navy Blue</span>
</div>
</div>
...
ProductGridComponent.jsx //显示每个类别的产品的位置。边栏是一个单独的组件。
class ProductGridComponent extends Component {
constructor(props) {
super(props);
const windowUrl = window.location.pathname.substring(1);
console.log("window url: ", windowUrl);
this.state = {
category: windowUrl.substring(windowUrl.indexOf("/") + 1), //Please fix me, I am vulnerable to SQL Injection
products: [],
};
console.log(this.state.category);
this.handleShopButtonClick = this.handleShopButtonClick.bind(this);
}
componentDidMount() {
ProductService.getProductsByCategory(this.state.category).then((res) => {
this.setState({ products: res.data });
});
}
handleShopButtonClick(productId) {
this.props.history.push(`/product/${productId}`);
}
onAddClick() {}
render() {
return (
<>
{/* <div className="container-fluid page-body-wrapper"> */}
<div className="wrapper">
<SideBarComponent />
<div className="row" style={{ marginLeft: "5px" }}>
{this.state.products.map((product) => (
<div className="col col-md-3" style={{ marginTop: "5px" }}>
<div className="card">
<div className="d-flex justify-content-between align-items-center">
<div className="d-flex flex-row align-items-center time">
<i className=""></i>
<small className="ml-1">{product.vendorName}</small>
</div>
</div>
<div className="text-center">
<img src="https://i.imgur.com/TbtwkyW.jpg" width="250" />
</div>
<div className="text-center">
<h5>{product.prodName}</h5>
<span className="text-success">${product.price}</span>
</div>
<div>
<Link to={`/product/${product.id}`}>
<button
className="btn btn-outline-dark flex-shrink-0"
type="button"
style={{ marginLeft: "10px" }}
>
<i
className="bi-bag-fill me-1"
style={{ marginRight: "4px" }}
></i>
Buy Now
</button>
</Link>
<Link to={`/product/${product.id}`}>
<button
className="btn btn-outline-dark flex-shrink-0"
type="button"
style={{ marginLeft: "10px" }}
>
<i className=""></i>
View
</button>
</Link>
</div>
</div>
</div>
))}
{/* <img src="https://i.imgur.com/aTqSahW.jpg" width="250" /> */}
</div>
</div>
</>
);
}
}
export default ProductGridComponent;
ProductViewComponent.jsx
class ProductViewComponent extends React.Component {
constructor(props) {
super(props);
const windowUrl = window.location.pathname.substring(1);
console.log("window url for product: ", windowUrl);
this.state = {
//id: this.props.match.params.id,
id: windowUrl.substring(windowUrl.indexOf("/") + 1), //Please fix me, I am vulnerable to SQL Injection
name: "",
price: 0,
vendor: "holder vendor",
description: "",
};
console.log("ID: ", this.state.id);
}
componentDidMount() {
ProductService.getProductById(this.state.id).then((res) => {
let product = res.data;
this.setState({
name: product.prodName,
price: product.price,
vendor: product.vendorName,
description: product.description,
});
});
}
render() {
return (
...
<button
className="btn btn-outline-dark flex-shrink-0"
type="button"
style={{ marginLeft: "10px" }}
onClick={() =>
this.props.onAddToCart(
this.state.id,
this.state.name,
this.state.description,
this.state.price
)
}
>
<i className="bi-cart-fill me-1"></i>
Add to cart
</button>
...
今天早些时候我遇到了类似的问题。我的 localStorage
在刷新时被重写,尽管 localStorage
通常用于在刷新/浏览器关闭之间传输数据。
我意识到的问题是我将 localStorage
设置为渲染时的状态,而我的状态被初始化为空值。看起来您只在代码中调用 localStorage.setItem()
一次,并将其设置为状态。问题是当 localStorage.setItem()
被调用时,你的状态是 仍然是一个空数组 .
React 的this.setState()
方法是一个异步方法,意味着它将被添加到一个堆栈中运行。它不承诺何时开始 运行 或何时结束。看起来你在调用 localStorage.setItem()
之前调用了 this.setState()
,这意味着它没有在你更改 localStorage
.
之前及时更新状态
我建议将对 localStorage
的调用放在 this.setState()
的第二个参数回调函数中。这个回调函数是总是运行状态设置后
您的状态 setter 将如下所示:
this.setState(
{
cart: [...this.state.cart, holder],
},
() => {
console.log("Updated Cart: ", this.state.cart);
localStorage.setItem("cart", JSON.stringify(this.state.cart));
}
);
问题是一个逻辑错误。重新加载时,状态(购物车)变为空。但是,在尝试将新商品添加到购物车之前,我从未将状态设置为本地存储中已有的商品。
我添加功能的方式涉及获取当前状态并附加新项目,然后设置本地存储。这意味着如果状态为空,则尝试以这种方式添加新商品只会导致购物车中只有新商品。
我把这一段加到App.js解决了错误
componentDidMount() {
const sCart = localStorage.getItem("cart");
const parsedCart = JSON.parse(sCart);
if (sCart == null) {
this.setState({ cart: [] });
} else {
console.log("cart String mount on shopping cart: ", sCart);
console.log("cart Object at mount on shopping cart: ", parsedCart);
this.setState(
{
cart: parsedCart,
}
);
}
}
我目前遇到的问题是,在尝试将属于不同页面(如下所示)的项目添加到本地存储后,本地存储被重置。本地存储功能正用于购物车功能。
当我尝试添加同一类别的项目时一切正常,但一旦我切换类别,本地存储就会被重置。
我还注意到一个奇怪的行为是,对于我尝试添加到购物车的第一个项目,我必须双击它才能在本地存储中注册。
我已经设置了程序,只有购物车页面需要访问本地存储。
正在添加“可穿戴设备类别”中的产品
返回并进入“计算机”部分中的项目。 忽略边栏
从可穿戴部分添加项目并清除本地存储。
代码: App.js
class App extends Component {
userData;
constructor(props) {
super(props);
this.state = {
cart: [],
};
this.handleAddToCart = this.handleAddToCart.bind(this);
}
handleAddToCart = (productId, prodName, description, price) => {
console.log(" Handle Add to Cart Called ", productId);
console.log("->cart state: ", this.state.cart);
const holder = {
productId,
quantity: 1,
prodName,
description,
price,
};
const idx = this.indexOfProduct(productId);
if (idx == -1) {
// Product does not exist in cart
this.setState(
{
cart: [...this.state.cart, holder],
},
() => {
console.log("Updated Cart: ", this.state.cart);
}
);
} else {
let newArray = [...this.state.cart];
newArray[idx] = {
...newArray[idx],
quantity: newArray[idx].quantity + 1,
};
this.setState(
{
cart: newArray,
},
() => {
console.log("Updated Cart: ", this.state.cart);
}
);
}
localStorage.setItem("cart", JSON.stringify(this.state.cart));
};
indexOfProduct(productId) {
for (let index = 0; index < this.state.cart.length; index++) {
if (this.state.cart[index].productId == productId) return index;
}
return -1;
}
render() {
return (
<div className="App">
{/* <div className="container-fluid">
<NavBarComponent />
</div> */}
<>
<Router>
<div className="container-fluid">
<NavBarComponent />
</div>
<Switch>
<Route exact path="/sidebar">
<SideBarComponent />
</Route>
<Route exact path="/products/:category">
<ProductGridComponent />
</Route>
<Route exact path="/cart">
<ShoppingCartComponent />
</Route>
<Route exact path="/product/:id">
{/*onAddToCart={this.handleAddToCart} */}
<ProductViewComponent onAddToCart={this.handleAddToCart} />
</Route>
<Route exact path="/contact">
<ContactUsComponent />
</Route>
<Route exact path="/about-us">
<AboutUsComponent />
</Route>
<Route exact path="/">
<HomeComponent />
</Route>
</Switch>
</Router>
</>
<FooterComponent />
</div>
);
}
}
export default App;
ShoppingCartComponent.jsx
class ShoppingCartComponent extends Component {
constructor(props) {
super(props);
this.state = {
cart: [],
};
console.log("Hello Im the constructor");
}
static getDerivedStateFromProps(props, state) {
console.log("Hello Im the dState Func");
const sCart = localStorage.getItem("cart");
const parsedCart = JSON.parse(sCart);
if (sCart == null) {
return { cart: [] };
} else {
console.log("cart String mount on shopping cart: ", sCart);
console.log("cart Object at mount on shopping cart: ", parsedCart);
return { cart: parsedCart };
console.log("After appending", this.state.cart);
}
}
render() {
console.log("Shopping Cart Array at Render(): ", this.state.cart);
return (
<div className="container mt-5 p-3 rounded cart">
<div className="row no-gutters">
<div className="col-md-8">
<div className="product-details mr-2">
<div className="d-flex flex-row align-items-center">
<i className="fa fa-arrow"></i>
<button /* onClick={history.back} */>
<span className="ml-2">
<a style={{ color: "black" }}>Continue Shopping</a>
</span>
</button>
</div>
<hr />
<h6 className="mb-0">Shopping cart</h6>
<div className="d-flex justify-content-between">
<span>
You have {this.state.cart.length} items in your cart
</span>
<div className="d-flex flex-row align-items-center">
<span className="text-black-50">Sort by:</span>
<div className="price ml-2">
<span className="mr-1">price</span>
<i className="fa fa-angle-down"></i>
</div>
</div>
</div>
{this.state.cart.map((product) => (
<div className="d-flex justify-content-between align-items-center mt-3 p-2 items rounded">
<div className="d-flex flex-row">
<img
className="rounded"
src="https://i.imgur.com/QRwjbm5.jpg"
width="40"
/>
<div className="ml-2">
<span className="font-weight-bold d-block">
{product.prodName}
</span>
<span className="spec">256GB, Navy Blue</span>
</div>
</div>
...
ProductGridComponent.jsx //显示每个类别的产品的位置。边栏是一个单独的组件。
class ProductGridComponent extends Component {
constructor(props) {
super(props);
const windowUrl = window.location.pathname.substring(1);
console.log("window url: ", windowUrl);
this.state = {
category: windowUrl.substring(windowUrl.indexOf("/") + 1), //Please fix me, I am vulnerable to SQL Injection
products: [],
};
console.log(this.state.category);
this.handleShopButtonClick = this.handleShopButtonClick.bind(this);
}
componentDidMount() {
ProductService.getProductsByCategory(this.state.category).then((res) => {
this.setState({ products: res.data });
});
}
handleShopButtonClick(productId) {
this.props.history.push(`/product/${productId}`);
}
onAddClick() {}
render() {
return (
<>
{/* <div className="container-fluid page-body-wrapper"> */}
<div className="wrapper">
<SideBarComponent />
<div className="row" style={{ marginLeft: "5px" }}>
{this.state.products.map((product) => (
<div className="col col-md-3" style={{ marginTop: "5px" }}>
<div className="card">
<div className="d-flex justify-content-between align-items-center">
<div className="d-flex flex-row align-items-center time">
<i className=""></i>
<small className="ml-1">{product.vendorName}</small>
</div>
</div>
<div className="text-center">
<img src="https://i.imgur.com/TbtwkyW.jpg" width="250" />
</div>
<div className="text-center">
<h5>{product.prodName}</h5>
<span className="text-success">${product.price}</span>
</div>
<div>
<Link to={`/product/${product.id}`}>
<button
className="btn btn-outline-dark flex-shrink-0"
type="button"
style={{ marginLeft: "10px" }}
>
<i
className="bi-bag-fill me-1"
style={{ marginRight: "4px" }}
></i>
Buy Now
</button>
</Link>
<Link to={`/product/${product.id}`}>
<button
className="btn btn-outline-dark flex-shrink-0"
type="button"
style={{ marginLeft: "10px" }}
>
<i className=""></i>
View
</button>
</Link>
</div>
</div>
</div>
))}
{/* <img src="https://i.imgur.com/aTqSahW.jpg" width="250" /> */}
</div>
</div>
</>
);
}
}
export default ProductGridComponent;
ProductViewComponent.jsx
class ProductViewComponent extends React.Component {
constructor(props) {
super(props);
const windowUrl = window.location.pathname.substring(1);
console.log("window url for product: ", windowUrl);
this.state = {
//id: this.props.match.params.id,
id: windowUrl.substring(windowUrl.indexOf("/") + 1), //Please fix me, I am vulnerable to SQL Injection
name: "",
price: 0,
vendor: "holder vendor",
description: "",
};
console.log("ID: ", this.state.id);
}
componentDidMount() {
ProductService.getProductById(this.state.id).then((res) => {
let product = res.data;
this.setState({
name: product.prodName,
price: product.price,
vendor: product.vendorName,
description: product.description,
});
});
}
render() {
return (
...
<button
className="btn btn-outline-dark flex-shrink-0"
type="button"
style={{ marginLeft: "10px" }}
onClick={() =>
this.props.onAddToCart(
this.state.id,
this.state.name,
this.state.description,
this.state.price
)
}
>
<i className="bi-cart-fill me-1"></i>
Add to cart
</button>
...
今天早些时候我遇到了类似的问题。我的 localStorage
在刷新时被重写,尽管 localStorage
通常用于在刷新/浏览器关闭之间传输数据。
我意识到的问题是我将 localStorage
设置为渲染时的状态,而我的状态被初始化为空值。看起来您只在代码中调用 localStorage.setItem()
一次,并将其设置为状态。问题是当 localStorage.setItem()
被调用时,你的状态是 仍然是一个空数组 .
React 的this.setState()
方法是一个异步方法,意味着它将被添加到一个堆栈中运行。它不承诺何时开始 运行 或何时结束。看起来你在调用 localStorage.setItem()
之前调用了 this.setState()
,这意味着它没有在你更改 localStorage
.
我建议将对 localStorage
的调用放在 this.setState()
的第二个参数回调函数中。这个回调函数是总是运行状态设置后
您的状态 setter 将如下所示:
this.setState(
{
cart: [...this.state.cart, holder],
},
() => {
console.log("Updated Cart: ", this.state.cart);
localStorage.setItem("cart", JSON.stringify(this.state.cart));
}
);
问题是一个逻辑错误。重新加载时,状态(购物车)变为空。但是,在尝试将新商品添加到购物车之前,我从未将状态设置为本地存储中已有的商品。 我添加功能的方式涉及获取当前状态并附加新项目,然后设置本地存储。这意味着如果状态为空,则尝试以这种方式添加新商品只会导致购物车中只有新商品。
我把这一段加到App.js解决了错误
componentDidMount() {
const sCart = localStorage.getItem("cart");
const parsedCart = JSON.parse(sCart);
if (sCart == null) {
this.setState({ cart: [] });
} else {
console.log("cart String mount on shopping cart: ", sCart);
console.log("cart Object at mount on shopping cart: ", parsedCart);
this.setState(
{
cart: parsedCart,
}
);
}
}