I am dispatching a redux action on a button click. When the button is clicked I then receive the error 'Invalid hook call. Hooks can only be called inside of the body of a function component.' I can have this same action be dispatched on the page render and it works fine. I have other actions being dispatched onClick but this one isn't working.
This is the action
import axios from "axios";
export const addReview = (productInfo) => async (dispatch) => {
try {
await axios({
method: "post",
url: url,
header: { "Content-Type": "application/json" },
data: {
name: productInfo.name,
rating: productInfo.rating,
description: productInfo.description,
buyAgain: productInfo.buyAgain,
productId: productInfo.productId,
},
}).then((response) => {
dispatch({
type: "PRODUCTS_SUCCESS",
payload: response.data,
});
});
} catch (error) {
dispatch({
type: "PRODUCTS_FAILURE",
payload: error,
});
}
};
And here is the action dispatch on onClick event
const submitReview = () => {
let productInfo = {
name,
rating: starRate,
description: message,
buyAgain,
productId: currentProduct,
};
dispatch(addReview(productInfo));
};
Here is the entire component
import React, { useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import "./ProductReview.css";
import { ImStarEmpty } from "react-icons/im";
import { ImStarFull } from "react-icons/im";
import { IoPersonCircle } from "react-icons/io5";
import { addReview } from "../../actions/addReview";
export const ProductReview = () => {
const [starRate, setStarRate] = useState();
const [message, setMessage] = useState();
const [buyAgain, setBuyAgain] = useState(true);
const [missingRating, setMissingRating] = useState(false);
const dispatch = useDispatch();
const fName = localStorage.getItem("firstName");
const currentProduct = useSelector(
(state) => state.userInfoReducer.currentProductReview
);
const allProducts = useSelector((state) => state.productReducer.data);
let productToDisplay;
let title;
let name;
allProducts.map((product) => {
if (product._id === currentProduct) {
productToDisplay = product;
if (product.title.length > 60) {
title = product.title.substring(0, 60) + "...";
}
}
return productToDisplay;
});
const submitReview = () => {
let productInfo = {
name,
rating: starRate,
description: message,
buyAgain,
productId: currentProduct,
};
dispatch(addReview(productInfo));
};
return (
<>
<div className="product-review-container">
<h1 className="product-review-title">Create Review</h1>
<div className="product-review-product">
<img
className="review-product-img"
src={productToDisplay.image}
alt="product"
/>
<h2 className="review-product-title">{title}</h2>
</div>
<hr className="line-break" />
<div className="review-product-review">
<div className="review-product-review-container">
<h2 className="review-product-review-title">Overall Rating</h2>
<p className="review-product-review-stars">
{starRate >= 1 ? (
<ImStarFull
className="star-full"
onClick={() => setStarRate(1)}
/>
) : (
<ImStarEmpty
className="star-empty"
onClick={() => setStarRate(1)}
/>
)}
{starRate >= 2 ? (
<ImStarFull
className="star-full"
onClick={() => setStarRate(2)}
/>
) : (
<ImStarEmpty
className="star-empty"
onClick={() => setStarRate(2)}
/>
)}
{starRate >= 3 ? (
<ImStarFull
className="star-full"
onClick={() => setStarRate(3)}
/>
) : (
<ImStarEmpty
className="star-empty"
onClick={() => setStarRate(3)}
/>
)}
{starRate >= 4 ? (
<ImStarFull
className="star-full"
onClick={() => setStarRate(4)}
/>
) : (
<ImStarEmpty
className="star-empty"
onClick={() => setStarRate(4)}
/>
)}
{starRate >= 5 ? (
<ImStarFull
className="star-full"
onClick={() => setStarRate(5)}
/>
) : (
<ImStarEmpty
className="star-empty"
onClick={() => setStarRate(5)}
/>
)}
</p>
{missingRating ? (
<p className="missing-rating">! Must choose a rating</p>
) : (
""
)}
</div>
<button
className="review-product-review-clear"
onClick={() => setStarRate(0)}
>
Clear
</button>
</div>
<div className="review-product-buy-again">
<h2 className="review-product-buy-again-title">
Would you buy this item again?
</h2>
<div className="btn-group btn-group-toggle" data-toggle="buttons">
<label className="btn btn-secondary active">
<input
type="radio"
name="options"
id="option1"
autoComplete="off"
onClick={() => setBuyAgain(true)}
/>{" "}
Yes
</label>
<label className="btn btn-secondary">
<input
type="radio"
name="options"
id="option2"
autoComplete="off"
onClick={() => setBuyAgain(false)}
/>{" "}
No
</label>
</div>
</div>
<hr className="line-break" />
<div className="review-product-message">
<h2 className="review-product-message-title">
Add a written message
</h2>
<input
type="text"
className="form-control review-product-message-input"
placeholder="What did you like or dislike?"
onChange={(e) => setMessage(e.target.value)}
/>
<h2 className="review-product-name-title">Choose your public name</h2>
<p className="review-product-appear">
This is how you'll appear to other customers
</p>
<div className="review-product-user-info">
<IoPersonCircle className="review-product-profile" />
<input
type="text"
className="form-control review-product-profile-name"
defaultValue={fName}
onChange={(e) => (name = e.target.value)}
/>
</div>
<hr className="line-break" />
<button
className="review-product-submit"
onClick={() => submitReview()}
>
Submit
</button>
{missingRating ? (
<p className="missing-rating">Missing information</p>
) : (
""
)}
</div>
</div>
</>
);
};
Out of sync versions of React and React DOM
You can run npm ls react-dom in your react projet and if the version is less then 16.8.0 it does not yet support Hooks
2- Do not call Hooks in event handlers.
Your hooks must always be at the top of your functional Component. You may be able to resolve this by simply moving down the initialization of ‘fName’ below your hooks.
Related
How can I create an order of adding items. I should be able to add one after the other item during on click. By default it should display the Textarea first and then blockquote ( see below )
a) When a user click on Text area button, it should add one after the blockquote.
b) Then when the user clicks on Code area button, it should add after Textarea. Could someone please advise ?
CSB link: https://codesandbox.io/s/distracted-worker-26jztf?file=/src/App.js
Something similar >> Expected behaviour: https://jsfiddle.net/nve8qdbu/8/
import "./styles.css";
import React, { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { useForm } from "react-hook-form";
const blogListData = [
{
id: 1,
heading: "React state",
date: "22-May-2022",
tag: "React",
count: "3"
},
{
id: 2,
heading: "Cypress testing detailss",
date: "22-May-2022",
tag: "Cypress",
count: "5"
}
];
const Admin = () => {
const [createImageTag, setImageTag] = useState("");
const [fields, setFields] = useState([{ value: null }]);
const [createCode, setCreateCode] = useState([{ value: null }]);
const [blogList, setBlogList] = useState([]);
const {
register,
handleSubmit,
formState: { errors },
reset
} = useForm();
useEffect(() => {
setBlogList(blogListData);
}, []);
function handleChangeTextArea(i, event) {
const values = [...fields];
values[i].value = event.target.value;
setFields(values);
}
function handleChangeCode(i, event) {
const codeValues = [...createCode];
codeValues[i].value = event.currentTarget.innerText;
setCreateCode(codeValues);
}
function handleTextAreaAdd() {
const values = [...fields];
values.push({ value: null });
setFields(values);
}
function handleCodeAreaAdd() {
const codeValues = [...createCode];
codeValues.push({ value: null });
setCreateCode(codeValues);
}
function handleImageAreaAdd() {
const image = [...createImageTag];
image.push({ value: null });
setCreateCode(image);
}
function handleRemoveText(i) {
const values = [...fields];
values.splice(i, 1);
setFields(values);
}
function handleRemoveCode(i) {
const codeValues = [...createCode];
codeValues.splice(i, 1);
setCreateCode(codeValues);
}
const handleLogout = () => {
localStorage.removeItem("loginEmail");
};
return (
<div id="App">
<div className="parent">
<div className="adminSection">
<h1>Create a new blog</h1>
<div className="row">
<div className="logout">
<img
src="/images/logout.png"
alt="Logout"
onClick={handleLogout}
></img>
</div>
<div className="createBlogSection">
<div className="row">
<button
onClick={() => handleTextAreaAdd()}
className="textAreaBtn"
>
Text Area
</button>
<button
onClick={() => handleCodeAreaAdd()}
className="codeAreaBtn"
>
Code Area
</button>
<button
onClick={() => handleImageAreaAdd()}
className="imageAreaBtn"
>
Add Image
</button>
</div>{" "}
<br></br>
<div className="row">
{fields.map((field, idx) => {
return (
<div key={`${field}-${idx}`} className="dtextArea">
<button
type="button"
onClick={() => handleRemoveText(idx)}
className="closeElement"
>
X
</button>
<textarea
type="text"
id="blogtext"
placeholder="Enter your text here"
className="defaultTextArea"
{...register("blogtext", {
required: true,
minLength: {
value: 25,
message: "Minimum length of 25 letters"
}
})}
value={field.value || ""}
onChange={(e) => handleChangeTextArea(idx, e)}
/>
<span className="validationmsg">
{errors.blogtext &&
errors.blogtext.type === "required" && (
<span>Blog text is required !</span>
)}
{errors.blogtext && (
<span>{errors.blogtext.message}</span>
)}
</span>
</div>
);
})}
</div>
<div className="row">
{createCode.map((code, idx) => {
return (
<div key={`${code}-${idx}`} className="dCodeArea">
<button
type="button"
onClick={() => handleRemoveCode(idx)}
className="closeElement"
>
X
</button>
<blockquote
type="text"
id="blogCode"
contentEditable="true"
className="codehighlight"
placeholder="Enter your code here"
{...register("blogCode", {
required: true
})}
value={code.value || ""}
onInput={(e) => handleChangeCode(idx, e)}
/>
</div>
);
})}
</div>
<div className="row">
</div>
<div className="row">
<div className="submitSection">
<input type="submit" className="submitBtn" />
</div>
</div>
</div>
</div>
</div>
<div className="blogListSection">
<h1>Edit blogs</h1>
<div className="row">
<div className="editBlogSection">
{blogList.map(({ id, heading, count }) => (
<a
key={id}
href="https://www.testingsite.com/"
className="blogitems"
>
<pre>
<span>{count}</span> {heading}
</pre>
</a>
))}
</div>
</div>
</div>
</div>
</div>
);
};
export default Admin;
react is designed for components . each of your list elements should be refactored by a component.then it would be easier. i think a single react component could do the trick
I'm trying to submit reviews to a directus app but the review is not getting added to the data.
I can retrieve the data but cannot add.
I've checked all permissions and set the directus app to public permissions.
cannot figure out what the problem could be.
can anyone advise what could be wrong?
the api call:
import axios from 'axios';
const url = 'https://pwsbbqhj.directus.app/items/Reviews';
export const readReviews = () => axios.get(url);
export const createReview = (newReview) => axios.post(url, newReview);
the data retrieval :
import React, { useState, useEffect } from 'react';
import * as api from '../../../api';
import { FaStar } from 'react-icons/fa';
const colors = {
orange: '#e42320',
grey: '#a9a9a9',
};
function Ratings() {
const stars = Array(5).fill(0);
const [currentValue, setCurrentValue] = React.useState(0);
const [hoverValue, setHoverValue] = React.useState(undefined);
const handleClick = (value) => {
setCurrentValue(value);
};
const handleMouseOver = (value) => {
setHoverValue(value);
};
const [review, setReview] = useState({});
const [reviews, setReviews] = useState([]);
useEffect(() => {
const fetchData = async () => {
const result = await api.readReviews();
setReviews(result.data.data);
};
fetchData();
}, []);
const createReview = async (review) => {
try {
const data = await api.createReview({ review });
setReview([...reviews, data.data]);
} catch (error) {
console.log(error);
}
};
let [reviewCount, setreviewCount] = useState([]);
const setCountFxn = (no) => {
setReview(no);
console.log(no);
};
return (
<div className='col-md-12 row-testimonials'>
<div className='reviews-heading pb-5 pt-5'>
<h2>REVIEWS FROM OUR CUSTOMERS</h2>
</div>
<div
className='themesflat-carousel-box has-bullets bullet-circle has-buttons has-arrows clearfix'
data-gap={30}
data-column={3}
data-column2={2}
data-column3={1}
data-auto='false'
>
<div className='owl-carousel owl-theme'>
{reviews.map((review) => (
<div className='themesflat-testimonials style-2 align-center clearfix' key={review.id}>
<div className='testimonial-item'>
<div className='inner'>
<div className='thumb'>
<img src='assets/img/testimonials/customer-1-90x90.jpg' alt='altimage' />
</div>
<blockquote className='text'>
<div className='name-pos clearfix'>
<h6 className='name'>{review.Title}</h6>
<span className='position'></span>
</div>
<p>{review.Body}</p>
<div className='list-star'>
{Array.from({ length: review.Rating }).map((i) => (
<FaStar key={i} size={18} color={colors.orange} />
))}
</div>
<div className='m-2'>
By: <span className='name'>{review.Name}</span>
</div>
</blockquote>
</div>
</div>
</div>
))}
</div>
</div>
<div className='bg-black'>
<form>
<div className='mb-5'>
<h2>RATE OUR SERVICE</h2>
<div className='mt-5 mb-5'>
{stars.map((_, index) => {
return (
<FaStar
key={index}
size={20}
style={{
marginRight: 10,
cursor: 'pointer',
}}
color={(hoverValue || currentValue) > index ? colors.orange : colors.grey}
onClick={() => {
setReview({ ...review, Rating: index + 1 });
}}
onMouseOver={() => handleMouseOver(index + 1)}
/>
);
})}
</div>
<div id='message'></div>
<div>
<input
type='text'
placeholder='Your Name'
required
value={review.Name}
onChange={(e) => setReview({ ...review, Name: e.target.value })}
className='wpcf7-form-control'
/>
</div>
<div>
<input
type='text'
placeholder='Review Title'
required
value={review.Title}
onChange={(e) => setReview({ ...review, Title: e.target.value })}
className='wpcf7-form-control'
/>
</div>
<textarea
placeholder='Your comment'
required
value={review.Body}
onChange={(e) => setReview({ ...review, Body: e.target.value })}
className='wpcf7-form-control'
/>
<button type='submit' className='submit wpcf7-form-control wpcf7-submit' onClick={createReview}>
submit
</button>
</div>
</form>
</div>
<div className='themesflat-spacer clearfix' data-desktop={80} data-mobile={60} data-smobile={60} />
</div>
);
}
export default Ratings;
previous issue
building a rating app using strapi and react throws errors is solved.
But, the records are not getting added to the admin.
can anyone help on this?
This is the code to add and read reviews from strapi admin,
function App() {
const stars = Array(5).fill(0);
const [currentValue, setCurrentValue] = React.useState(0);
const [hoverValue, setHoverValue] = React.useState(undefined);
const handleClick = (value) => {
setCurrentValue(value);
};
const handleMouseOver = (value) => {
setHoverValue(value);
};
const [review, setReview] = useState({});
const [reviews, setReviews] = useState([]);
useEffect(() => {
const fetchData = async () => {
const result = await api.readReviews();
//console.log(result.data);
setReviews(result.data.data);
};
fetchData();
}, []);
const createReview = async () => {
try {
//console.log(review);
const data = await api.createReview(review);
setReview([...reviews, data]);
} catch (error) {
//console.log(error);
}
};
let [reviewCount, setreviewCount] = useState([]);
const setCountFxn = (no) => {
setReview(no);
};
return (
<>
<form>
<div style={styles.container}>
<h2>RATE OUR SERVICE</h2>
<div style={styles.stars}>
{stars.map((_, index) => {
return (
<FaStar
key={index}
size={24}
style={{
marginRight: 10,
cursor: 'pointer',
}}
color={(hoverValue || currentValue) > index ? colors.orange : colors.grey}
onClick={() => {
setReview({ ...review, Rating: index + 1 });
}}
onMouseOver={() => handleMouseOver(index + 1)}
/>
);
})}
</div>
<div>
<input
type='text'
placeholder='input your name'
required
style={styles.input}
value={review.Name}
onChange={(e) => setReview({ ...review, Name: e.target.value })}
/>
</div>
<textarea
placeholder="what's your feedback"
required
style={styles.textarea}
value={review.review}
onChange={(e) => setReview({ ...review, review: e.target.value })}
/>
<button type='submit' style={styles.button} className='btn btn-primary' onClick={createReview}>
submit
</button>
</div>
</form>
<section id='reviews'>
<div className='reviews-heading'>
<span>REVIEWS FROM CUSTOMERS</span>
</div>
<div className='container'>
<div className='row'>
{reviews.map((review, i) => (
<div key={review.id} className='col-md-6'>
<div className='reviews-box'>
<div className='box-top'>
<div className='profile'>
<div className='name-user'>
<strong>{review.attributes.Title}</strong>
</div>
</div>
<div style={styles.stars}>
{Array.from({ length: review.attributes.Rating }).map((i) => (
<FaStar key={i} size={18} color={colors.orange} />
))}
</div>
</div>
<div className='client-comment'>{review.attributes.Body}</div>
</div>
</div>
))}
</div>
</div>
</section>
</>
);
}
export default App;
The form gets submitted and reloads after submit, but the record does not get added to strapi admin. I've set the roles of the data to public.
thanks
Nabi
You need a very specific structure to create a new entry with strapi. I assume your payload looks like this:
{
name: 'xyz',
rating: 5
}
Correct would be:
{
data: {
name: 'xyz',
rating: 5
}
}
Strapi Docs - Creating an entry
Ive figured it out!
The values of the form were named wrong. "Name" should be "Title" and "review" should be "Body". It now adds the data to the strapi admin.
I'm trying to convert the class components in my Reactjs ToDo app to functional components. Here is one of the my original components:
import React, { Component } from "react";
import TaskDataService from "../services/task.service";
import FadeIn from 'react-fade-in';
import { Link } from "react-router-dom";
export default class TasksList extends Component {
constructor(props) {
super(props);
this.onChangeSearchTitle = this.onChangeSearchTitle.bind(this);
this.retrieveTasks = this.retrieveTasks.bind(this);
this.refreshList = this.refreshList.bind(this);
this.setActiveTask = this.setActiveTask.bind(this);
this.removeAllTasks = this.removeAllTasks.bind(this);
this.searchTitle = this.searchTitle.bind(this);
this.state = {
Tasks: [],
currentTask: null,
currentIndex: -1,
searchTitle: ""
};
}
componentDidMount() {
this.retrieveTasks();
}
onChangeSearchTitle(e) {
const searchTitle = e.target.value;
this.setState({
searchTitle: searchTitle
});
}
retrieveTasks() {
TaskDataService.getAll()
.then(response => {
this.setState({
Tasks: response.data
});
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
refreshList() {
this.retrieveTasks();
this.setState({
currentTask: null,
currentIndex: -1
});
}
setActiveTask(Task, index) {
this.setState({
currentTask: Task,
currentIndex: index
});
}
removeAllTasks() {
TaskDataService.deleteAll()
.then(response => {
console.log(response.data);
this.refreshList();
})
.catch(e => {
console.log(e);
});
}
searchTitle() {
this.setState({
currentTask: null,
currentIndex: -1
});
TaskDataService.findByTitle(this.state.searchTitle)
.then(response => {
this.setState({
Tasks: response.data
});
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
render() {
const { searchTitle, Tasks, currentTask, currentIndex } = this.state;
return (
<FadeIn>
<div className="list row">
<div className="col-md-8">
<div className="input-group mb-3">
<input
type="text"
className="form-control"
placeholder="Search by title"
value={searchTitle}
onChange={this.onChangeSearchTitle}
/>
<div className="input-group-append">
<button
className="btn btn-outline-secondary"
type="button"
onClick={this.searchTitle}
>
Search
</button>
</div>
</div>
</div>
<div className="col-md-6">
<h4>Tasks List</h4>
<FadeIn>
<ul className="list-group">
{Tasks &&
Tasks.map((Task, index) => (
<li
className={
"list-group-item " +
(index === currentIndex ? "active" : "")
}
onClick={() => this.setActiveTask(Task, index)}
key={index}
>
<div className="align-left">
{Task.title}
</div>
<div className="align-right">
{Task.startDate.toString().split("T")[0]}
</div>
</li>
))}
</ul>
</FadeIn>
<button
className="m-3 btn btn-sm btn-danger"
onClick={this.removeAllTasks}
>
Remove All
</button>
</div>
<div className="col-md-6">
{currentTask ? (
<FadeIn>
<div>
<h4>Task</h4>
<div>
<label>
<strong>Title:</strong>
</label>{" "}
{currentTask.title}
</div>
<div>
<label>
<strong>Description:</strong>
</label>{" "}
{currentTask.description}
</div>
<div>
<label>
<strong>Status:</strong>
</label>{" "}
{currentTask.completed ? "Completed" : "Pending"}
</div>
<div>
<label>
<strong>Due Date:</strong>
</label>{" "}
{currentTask.startDate.split("T")[0]}
</div>
<Link
to={"/Tasks/" + currentTask.id}
className="badge badge-warning"
>
Edit
</Link>
</div>
</FadeIn>
) : (
<div>
<br />
<p>Please click on a Task...</p>
</div>
)}
</div>
</div>
</FadeIn>
);
}
}
...and my attempt to convert it into a functional component:
import React, { useState, useEffect } from "react";
import TaskDataService from "../services/task.service";
import FadeIn from 'react-fade-in';
import { Link } from "react-router-dom";
function TasksList() {
const [Tasks, setTasks] = useState([]);
const [currentTask, setCurrentTask] = useState(null);
const [currentIndex, setCurrentIndex] = useState(-1);
const [searchTitle, setSearchTitle] = useState("");
useEffect(() => {
retrieveTasks();
});
const onChangeSearchTitle = (e) => {
const searchTitle = e.target.value;
setSearchTitle({
searchTitle: searchTitle
});
}
const retrieveTasks = () => {
TaskDataService.getAll()
.then(response => {
setTasks({
Tasks: response.data
});
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
const refreshList = () => {
retrieveTasks();
setTasks(null);
setCurrentIndex(-1);
}
const setActiveTask = (Task, index) => {
setTasks( Task );
setCurrentIndex( index );
}
const removeAllTasks = () => {
TaskDataService.deleteAll()
.then(response => {
console.log(response.data);
refreshList();
})
.catch(e => {
console.log(e);
});
}
const onSearchTitle = () => {
setTasks(null);
setCurrentIndex(-1);
TaskDataService.findByTitle(searchTitle)
.then(response => {
setTasks(response.data)
console.log(response.data);
})
.catch(e => {
console.log(e);
});
}
return (
<FadeIn>
<div className="list row">
<div className="col-md-8">
<div className="input-group mb-3">
<input
type="text"
className="form-control"
placeholder="Search by title"
value={searchTitle}
onChange={onChangeSearchTitle}
/>
<div className="input-group-append">
<button
className="btn btn-outline-secondary"
type="button"
onClick={onSearchTitle}
>
Search
</button>
</div>
</div>
</div>
<div className="col-md-6">
<h4>Tasks List</h4>
<FadeIn>
<ul className="list-group">
{Tasks &&
Tasks.map((Task, index) => (
<li
className={
"list-group-item " +
(index === currentIndex ? "active" : "")
}
onClick={() => setActiveTask(Task, index)}
key={index}
>
<div className="align-left">
{Task.title}
</div>
<div className="align-right">
{Task.startDate.toString().split("T")[0]}
</div>
</li>
))}
</ul>
</FadeIn>
<button
className="m-3 btn btn-sm btn-danger"
onClick={removeAllTasks}
>
Remove All
</button>
</div>
<div className="col-md-6">
{currentTask ? (
<FadeIn>
<div>
<h4>Task</h4>
<div>
<label>
<strong>Title:</strong>
</label>{" "}
{currentTask.title}
</div>
<div>
<label>
<strong>Description:</strong>
</label>{" "}
{currentTask.description}
</div>
<div>
<label>
<strong>Status:</strong>
</label>{" "}
{currentTask.completed ? "Completed" : "Pending"}
</div>
<div>
<label>
<strong>Due Date:</strong>
</label>{" "}
{currentTask.startDate.split("T")[0]}
</div>
<Link
to={"/tasks/" + currentTask.id}
className="badge badge-warning"
>
Edit
</Link>
</div>
</FadeIn>
) : (
<div>
<br />
<p>Please click on a Task...</p>
</div>
)}
</div>
</div>
</FadeIn>
);
}
export default TasksList;
But when running my frontend, the page fails to load with this error seeming like the big one: "Uncaught TypeError: Tasks.map is not a funtion". Console points to this segment as the culprit:
<ul className="list-group">
{Tasks &&
Tasks.map((Task, index) => (
<li...
So it doesn't like Tasks.map. I've spent a lot of time on research and can't figure out what the problem is. Any ideas?
It seems that you are settings in multiple part of your code with setTasks a value other than an array which eventually can leads to Tasks.map triggering an error.
For exemple in the function retreiveTasks you are setting Tasks to an object.
I want to submit and close with the button "save". change type to "button" type="submit" value="submit" onClick={ onClose } don't submit and don't close. I am using onClose for close the modal
import { useAppDispatch } from '../redux/hooks';
import { userInfo } from '../redux/model';
import { useForm } from 'react-hook-form';
import { ModalProps } from '../redux/model';
import { setList } from '../redux/slice';
type UI = userInfo;
const AddData: React.FC<ModalProps> = ({ isOpen, onClose }) => {
const [ page, setPage ] = useState(0);
const FormTitles = ["Invoice Address", "Bank Data", "Contact"];
const dispatch = useAppDispatch();
const { register, handleSubmit, formState: { errors, isValid }} = useForm<UI>({ mode: "all" });
const onSubmit = handleSubmit((data) => {
dispatch(setList(data));
});
return isOpen ? (<div className="viewModal">
<div className='modalContent'>
<form onSubmit={ onSubmit }>
<div className="contentForm">
<div className="closeX" onClick={ onClose }>x</div>
<div className="labels">
<div className="titleTxt">{ FormTitles[page] }</div>
{ page === 0 && (<>
<div className="labelInput">
<label htmlFor="additional">Additional</label>
<input { ...register("additional")} id="additional" />
</div>
<div className="labelInput">
... // content
</div>
<div className="labelInput">
... // content
</div>
</>)}
{ page === 1 && (<>
<div className="labelInput">
<label htmlFor="iban">IBAN</label>
<div className="dirCol">
<input { ...register("iban", { required: true, maxLength: 30 })} id="iban" />
<div className="required">{ errors.iban && "This is required!" }</div>
</div>
</div>
<div className="labelInput">
... // content
</div>
{ page === 2 && (<>
{/* *** fax *** */}
<div className="labelInput">
<label htmlFor="fax">Fax</label>
<input { ...register("fax")} id="fax" />
</div>
<div className="labelInput">
... // content
</div>
<div className="labelInput">
... // content
</div>
</div>
<div className="labelButton">
<button className="button2" onClick={ onClose }>Cancel</button>
{ page !== 0 && (<div className="button2 textCancel" onClick={() => { setPage((x) => x - 1)}}
>Previous</div>)}
{ page !== 2 && (<button className="button1" type="button" disabled={ !isValid } onClick={() => { setPage((x) => x + 1)}}
>Next</button>)}
{ page === 2 && (<>
<button className="button1" type="submit" value="submit" onClick={ onClose }>Save</button>
</>)}
</div>
</div>
</form>
</div>
</div>) : null
};
export default AddData;
here AddData off and on, onClose working with cancel and x button
import { useState } from 'react';
import AddData from './AddData';
import { useAppDispatch, useAppSelector } from '../redux/hooks';
import { removeList } from '../redux/slice';
const ListTable = () => {
const [ isModalOpen, setIsModalOpen ] = useState(false);
const toogleModal = () =>setIsModalOpen(!isModalOpen);
const dispatch = useAppDispatch();
const selector = useAppSelector((state) => state.users.list );
return <>
<AddData isOpen={ isModalOpen } onClose={ toogleModal }>
</AddData>
<nav>
<button onClick={ toogleModal }>Add</button>
</nav>
<div className='content'>
... // content
</div>
</>
};
export default ListTable;
Inside of the onClick for your save button you can call onSubmit and onClose.
import { useAppDispatch } from "../redux/hooks";
import { userInfo } from "../redux/model";
import { useForm } from "react-hook-form";
import { ModalProps } from "../redux/model";
import { setList } from "../redux/slice";
type UI = userInfo;
const AddData: React.FC<ModalProps> = ({ isOpen, onClose }) => {
const [page, setPage] = useState(0);
const FormTitles = ["Invoice Address", "Bank Data", "Contact"];
const dispatch = useAppDispatch();
const {
register,
handleSubmit,
formState: { errors, isValid },
} = useForm<UI>({ mode: "all" });
const onSubmit = handleSubmit((data) => {
dispatch(setList(data));
});
return isOpen ? (
<div className="viewModal">
<div className="modalContent">
<form onSubmit={onSubmit}>
<div className="contentForm">
<div className="closeX" onClick={onClose}>
x
</div>
<div className="labels">
<div className="titleTxt">{FormTitles[page]}</div>
{page === 0 && (
<>
<div className="labelInput">
<label htmlFor="additional">Additional</label>
<input {...register("additional")} id="additional" />
</div>
<div className="labelInput">{/* ... // content */}</div>
<div className="labelInput">{/* ... // content */}</div>
</>
)}
{page === 1 && (
<>
<div className="labelInput">
<label htmlFor="iban">IBAN</label>
<div className="dirCol">
<input
{...register("iban", { required: true, maxLength: 30 })}
id="iban"
/>
<div className="required">
{errors.iban && "This is required!"}
</div>
</div>
</div>
<div className="labelInput">... // content</div>
</>
)}
{page === 2 && (
<>
{/* *** fax *** */}
<div className="labelInput">
<label htmlFor="fax">Fax</label>
<input {...register("fax")} id="fax" />
</div>
<div className="labelInput">{/* ... // content */}</div>
<div className="labelInput">{/* ... // content */}</div>
</>
)}
</div>
<div className="labelButton">
<button className="button2" onClick={onClose}>
Cancel
</button>
{page !== 0 && (
<div
className="button2 textCancel"
onClick={() => {
setPage((x) => x - 1);
}}
>
Previous
</div>
)}
{page !== 2 && (
<button
className="button1"
type="button"
disabled={!isValid}
onClick={() => {
setPage((x) => x + 1);
}}
>
Next
</button>
)}
{page === 2 && (
<>
<button
className="button1"
type="submit"
value="submit"
onClick={() => {
onSubmit();
onClose;
}}
>
Save
</button>
</>
)}
</div>
</div>
</form>
</div>
</div>
) : null;
};
export default AddData;