how to hide table rows based on an input in react js - javascript

i created an app using react js which get te data from a graphql endpoint .
the data is displayed in a table and i ant to add a search function .
when the attribute name match the input in the search field only the roes that contains that name will stay displayed so basically i ant to hide the other rows
search component :
import React, { useState } from 'react'
const Search = ({ getQuery }) => {
const [text, setText] = useState('')
const onChange = (q) => {
setText(q)
getQuery(q)
}
return (
<section className='search'>
<form>
<input
type='text'
className='form-control'
placeholder='Search characters'
value={text}
onChange={(e) => onChange(e.target.value)}
autoFocus
/>
</form>
</section>
)
}
export default Search
orders list :
import React , { useState, useEffect } from 'react'
import { gql, useQuery } from '#apollo/client';
import Table from 'react-bootstrap/Table'
import Moment from 'react-moment';
import moment from "moment";
import { Link } from 'react-router-dom';
import Row from 'react-bootstrap/Row'
import Col from 'react-bootstrap/Col'
import { DangerousChangeType } from 'graphql';
import Search from './Search'
import Button from 'react-bootstrap/Button'
const GET_All_Orders = gql`
query Orders($input1: PaginationInput) {
Orders(input: $input1){
pageInfo {
hasNextPage
hasPreviousPage
}
edges{
cursor
node {
id
closed
email
createdAt
updatedAt
cancelledAt
displayFinancialStatus
displayFulfillmentStatus
lineItems{
edges{
node {
customAttributes{
key
value
}
id
quantity
title
variant{
id
image {
altText
id
src
}
title
weight
weightUnit
price
}
}
}
}
shippingAddress {
name
}
phone
subtotalPrice
totalPrice
totalRefunded
totalTax
processedAt
}
}
}
}
`;
export default function AllOrders({ input1 }) {
const { loading, error, data , fetchMore} = useQuery(GET_All_Orders, {
variables: {"input1": {
"num": 20,
}}
,
});
let date = new Date()
const [query, setQuery] = useState('')
if (loading) return <h4>読み込み中...</h4>;
if (error) return `Error! ${error}`;
return( <div>
<Row >
<Col xs={10}> <h5>すべての注文</h5></Col>
<Col><h5> 日付 : <Moment format="YYYY/MM/DD">
{ date}
</Moment> </h5></Col>
</Row>
<br/>
<Table responsive hover size="sm">
<thead>
<tr>
<th className="allOrders">注文日</th>
<th className="allOrders">名前</th>
<th className="allOrders">注文者メールアドレス</th>
<th className="allOrders" >配送状態</th>
<th className="allOrders" >支払状況</th>
<th className="allOrders" >合計金額</th>
<th className="allOrders" >詳細</th>
</tr>
</thead>
<tbody>
{data.Orders.edges.map(({ edges ,node :{id , createdAt , displayFulfillmentStatus , displayFinancialStatus , totalPrice , email , shippingAddress: {
name
} }}) => (
<tr key={id}>
<td> <Moment format="YYYY/MM/DD">
{createdAt}
</Moment></td>
<td>{ name} </td>
<td>{ email} </td>
{displayFulfillmentStatus == "FULFILLED" ? <td className="success">配送済み</td> : <td className="failed">未配送</td>}
{displayFinancialStatus == "PAID" ? <td>支払済み</td> : <td>未払い</td> }
<td>{totalPrice} </td>
<td>
<Link to={`/orders/${id}`} className="btn btn-light">
詳細
</Link></td>
</tr>
))}
</tbody>
</Table>
<div className="text-center">
<Button
variant="light"
onClick={() => {
fetchMore({
variables: {"input1": {
"num": 20,
"cursor": data.Orders.edges[data.Orders.edges.length - 1].cursor }},
updateQuery: (prevResult, { fetchMoreResult }) => {
fetchMoreResult.Orders.edges = [
...prevResult.Orders.edges,
...fetchMoreResult.Orders.edges
];
return fetchMoreResult;
}
});
}}
>
もっと
</Button>
</div>
</div>
)}
when i try it inside the map function like :
{query === name ? <td> {name}</td> : null }
it works fine but i don't know how to deal with the rows and that logic .

Related

Delete Table Row not working properly with search bar

i'm new to reactjs and i'm trying to make a table that shows the information from a array of objects and have a button of delete and an input to search among the users. The delete button is working correctly when i'm not searching anything, but when i'm searching it doesn't delete the corretly row, and deletes only the first one. I see that it is because the arrays that show the table are different with and without the search being used but I don't know how to make it work.
this is the component of the table:
import { formatDate } from "../../utils/formatDate";
import "./table.css";
import { useState } from "react";
function Table(props) {
const { headerData, bodyData, type, removeItem} = props;
const isUser = type === "user";
const buildTableItems = () => {
return bodyData.map((item, index) => (
<tr className="data-tr">
<td>{item.name}</td>
<td>{item.email}</td>
<td>{item.occupation}</td>
<td>{formatDate(item.birthday)}</td>
<td>
<button className="delete-button" onClick={() => removeItem(index)}>
Delete
</button>
</td>
</tr>
));
};
return (
<div className="user-data">
<table className="user-table">
<thead>
<tr className="data-th">
{headerData.map((headerTable) => (
<th >{headerTable}</th>
))}
</tr>
</thead>
<tbody>{buildTableItems()}</tbody>
</table>
</div>
);
}
export default Table;
Here the component of the search bar:
import "./searchBar.css"
function SearchBar({ searchedData, onSearch }) {
return (
<div className="search-bar">
<label>Search</label>
<input type="text" placeholder="Search User" value={searchedData} onChange={e => onSearch(e.target.value)} />
</div>
);
}
export default SearchBar;
and here is the home:
import "./Home.css";
import React, { useEffect, useState } from "react";
import Header from "../components/Header/Header";
import Table from "../components/Table/Table";
import AddData from "../components/AddData/AddData";
import SearchBar from "../components/SearchBar/SearchBar";
import { userArr } from "../mock/users";
const Home = () => {
const headerUser = ["Name", "Email", "Occupation", "Birthday"];
const [newUserArr, setNewUserArr] = useState(userArr);
const [searchedItem, setSearchedItem] = useState("");
const searchedArray = newUserArr.filter((item) => {
if (item.name.toLowerCase().includes(searchedItem.toLowerCase())) {
return true;
}
});
function onSearch(e) {
setSearchedItem(e);
}
const addDataToArr = (form) => {
setNewUserArr([...newUserArr, form]);
};
const deleteData = (indexUserArr) => {
let restOfDataArray = newUserArr.filter(
(element, ind) => ind !== indexUserArr
);
setNewUserArr(restOfDataArray);
};
return (
<>
<Header />
<SearchBar searchedData={searchedItem} onSearch={onSearch} />
<Table
type="user"
headerData={headerUser}
bodyData={newUserArr}
removeItem={(index) => deleteData(index)}
/>
<AddData saveData={(val) => addDataToArr(val)} />
</>
);
};
export default Home;
thank you
If you have ID in your user data then use that instead of index or create id keywords using concatenate with your values here is examples.
import { formatDate } from "../../utils/formatDate";
import "./table.css";
import { useState } from "react";
function Table(props) {
const { headerData, bodyData, type, removeItem} = props;
const isUser = type === "user";
const buildTableItems = () => {
return bodyData.map((item, index) => (
<tr className="data-tr">
<td>{item.name}</td>
<td>{item.email}</td>
<td>{item.occupation}</td>
<td>{formatDate(item.birthday)}</td>
<td>
<button className="delete-button" onClick={() => removeItem(`${item.name}${item.email}${item.occupation}`)}>
Delete
</button>
</td>
</tr>
));
};
return (
<div className="user-data">
<table className="user-table">
<thead>
<tr className="data-th">
{headerData.map((headerTable) => (
<th >{headerTable}</th>
))}
</tr>
</thead>
<tbody>{buildTableItems()}</tbody>
</table>
</div>
);
}
export default Table;
And here is your delete method ${item.name}${item.email}${index}
const deleteData = (data) => {
let restOfDataArray = newUserArr.filter(
(element, ind) => `${element.name}${element.email}${element.occupation}` !== data
);
setNewUserArr(restOfDataArray);
};
This will fixed your problem. If this doesn't work then you need to use ID to resolve this problem. There is a possibility that ${item.name}${item.email}${item.occupation} can be duplicates.
Never use index ever for deleting or any other operations. Use always ID.

Passing user input from child functional component to parent functional component

Im creating an invoice generator where the user can add an item, its price, and the quantity. I want to access the user inputs as a state from a child functional component (TableItems.js) into a parent functional component (TableSheet.js) to be able to save the user inputs into a database preferably firestore. I'm having a problem accessing the user input value from the child component to the parent component. I have been struggling with this bug for days, i really hope you guys could help me.
This is the Child component
import React, {useState, useEffect} from 'react'
function TableItems({index, tableItem }) {
const [price, setPrice] = useState(0);
const [qty, setQty] = useState(0);
const [total, setTotal] = useState([]);
useEffect(() => {
//arithmetically add price and qty values
const x = Number(price) * Number(qty)
setTotal(x)
return () => {
//clean up function will be here
};
}, [price, qty, total ]);
return (
<>
<tr>
<td><input type='text' required/></td>
<td><input type='number' value={price} onChange={(e) => setPrice(e.target.value)}/></td>
<td><input type='number' value={qty} onChange={(e) => setQty(e.target.value)}/></td>
<td>{total}</td>
</tr>
</>
)
}
export default TableItems
This is the Parent component
import React, { useState } from 'react'
import TableItems from './TableItems'
function TableSheet() {
const [tableItem, setTableItem] = useState([1]);
//adding a new table cell (table row)
const addCell = () => {
setTableItem((t) => [...t, t + 1])
}
return (
<div>
<table>
<thead>
<th>Item Description</th>
<th>Price</th>
<th>Qty.</th>
<th>Total</th>
</thead>
{
tableItem.map((tableItem, index, setItem) => {
return <TableItems key={index} tableItem={tableItem} setItem={setItem} addCell={addCell}/>
})
}
</table>
<button onClick={addCell}>+</button>
</div>
)
}
export default TableSheet
You tableItem state should contains item objects (quantity and price)
TableItems
function TableItems({ index, tableItem, onChangeItem }) {
return (
<>
<tr>
<td>
<input type="text" required />
</td>
<td>
<input
type="number"
value={tableItem.price}
onChange={(e) => onChangeItem(index, "price", e.target.value)}
/>
</td>
<td>
<input
type="number"
value={tableItem.quantity}
onChange={(e) => onChangeItem(index, "quantity", e.target.value)}
/>
</td>
<td>{Number(tableItem.price) * Number(tableItem.quantity)}</td>
</tr>
</>
);
}
TableSheet
function TableSheet() {
const [tableItem, setTableItem] = useState([
{
price: 0,
quantity: 0
}
]);
const onChangeItem = (index, type, value) => {
const newTable = tableItem.map((item, idx) => {
if (idx === index)
return {
...item,
[type]: value
};
return item;
});
setTableItem(newTable);
};
const addCell = () => {
setTableItem((t) => [
...t,
{
price: 0,
quantity: 0
}
]);
};
const totalPrice = tableItem.reduce((acc, cur) => {
acc += Number(cur.price) * Number(cur.quantity);
return acc;
}, 0);
return (
<div>
<table>
<thead>
<th>Item Description</th>
<th>Price</th>
<th>Qty.</th>
<th>Total</th>
</thead>
{tableItem.map((tableItem, index) => {
return (
<TableItems
key={index}
index={index}
tableItem={tableItem}
onChangeItem={onChangeItem}
/>
);
})}
</table>
<button onClick={addCell}>+</button>
<div>Total: {totalPrice}</div>
</div>
);
}
you can check in my codesandbox. Hope it help!

how to pass Project ID to another component in react.js

**Hello Guys, i create a table in MyProjectList.js file for displaying project data now i want, If user Click on table row then i want to pass this clicked Project Id to Collection.js file to use in query to compare.
*this is MyProjectList.js file.
import React from "react";
import { useEffect, useState } from "react";
import { Table } from "react-bootstrap";
import axios from "axios";
import _ from "lodash";
import { useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";
const pageSize = 10;
export default function MyProjectsList() {
let navigate = useNavigate();
const [projects, setProjects] = useState();
const [paginatedProject, setPaginatedProject] = useState();
const [currentPage, setCurrentPage] = useState(1);
const userState = useSelector(state=> state.loginUserReducer)
const {currentUser} =userState;
const coordinatorId = currentUser.id;
useEffect(() => {
axios.post("http://localhost:4000/coordinatorsProjects",
{
coordinatorId : coordinatorId
}).then((res) => {
console.log(res.data,'==>>> this is coordinator projects');
setProjects(res.data);
setPaginatedProject(_(res.data).slice(0).take(pageSize).value());
});
}, [coordinatorId]);
const pageCount = projects ? Math.ceil(projects.length / pageSize) : 0;
const pages = _.range(1, pageCount + 1);
const pagination = (pageNo) => {
setCurrentPage(pageNo);
const startIndex = (pageNo - 1) * pageSize;
const paginatedProject = _(projects)
.slice(startIndex)
.take(pageSize)
.value();
setPaginatedProject(paginatedProject);
};
//***using this function i am nevigate user to colletion.js component. how to pass project id this?
const onRowClick = async (e) => {
navigate("/coordinators/collection")
console.log(e)
}
return (
<>
<div className="container">
{/* {loading && (<Loading/>)} */}
{/* {error && alert("Error occured to get data")} */}
{!paginatedProject ? (
"ERROR: Data Not Found. Please check your internet connection!"
) : (
<Table className="table table-hover table-light table-bordered shadow">
<thead className="thead-dark">
<tr>
<th scope="col">Project Name</th>
<th scope="col">Start Date</th>
<th scope="col">End Date</th>
<th scope="col">Budget Rs.</th>
<th scope="col">Remaining Rs.</th>
</tr>
</thead>
<tbody>
{paginatedProject.map((user, _id) => (
<tr onClick={ onRowClick } key={_id}>
<td>{user.projectName}</td>
<td>{user.startDate}</td>
<td>{user.endDate}</td>
<td>{user.budget}</td>
<td>{user.remaining}</td>
</tr>
))}
</tbody>
</Table>
)}
<nav className="d-flex pagination justify-content-center ">
<ul className="pagination ">
{pages.map((page) => (
<li
className={
page === currentPage ? "page-item active" : "page-item"
}
>
<p className="page-link" onClick={() => pagination(page)}>
{page}
</p>
</li>
))}
</ul>
</nav>
</div>
</>
);
}
*this is Collection.js file.
import React from 'react';
import { useEffect, useState } from "react";
import { Table } from "react-bootstrap";
import axios from "axios";
import _ from "lodash";
const pageSize= 10;
export default function Collection () {
const [donors, setDonors] = useState()
const [paginatedDonors, setPaginatedDonors] = useState()
const [currentPage, setCurrentPage] = useState(1)
useEffect(() => {
axios.post("http://localhost:4000/coordinatorsCollection",
{
projectID : "7"
}).then((res)=>{
console.log(res.data);
setDonors(res.data);
setPaginatedDonors(_(res.data).slice(0).take(pageSize).value());
});
}, []);
const pageCount= donors? Math.ceil(donors.length/pageSize) :0;
const pages = _.range(1, pageCount+1)
const pagination= (pageNo) =>{
setCurrentPage(pageNo)
const startIndex = (pageNo -1) * pageSize;
const paginatedDonors = _(donors).slice(startIndex).take(pageSize).value();
setPaginatedDonors(paginatedDonors)
}
return (
<>
<div className='container'>
<h3 align="center">Collection of users</h3>
{/* {loading && (<Loading/>)} */}
{/* {error && alert("Error occured to get data")} */}
{!paginatedDonors ? ("ERROR: Data Not Found. Please check your internet connection!"):(
<Table className="table table-hover table-light table-bordered shadow">
<thead className="thead-dark">
<tr>
<th scope="col">Donor Name</th>
<th scope="col">Contact No</th>
<th scope="col">Amount</th>
<th scope="col">Project</th>
<th scope="col">Project Budget</th>
<th scope="col">Donate Date.</th>
</tr>
</thead>
<tbody >
{paginatedDonors.map((donors, id) => (
<tr key={id}>
<td>{donors.name}</td>
<td>{donors.mobileNo}</td>
<td>{donors.amount}</td>
<td>{donors.project}</td>
<td>{donors.projectBudget}</td>
<td>{donors.donateDate}</td>
</tr>
))}
</tbody>
</Table>
)}
<nav className="d-flex pagination justify-content-center ">
<ul className="pagination ">
{
pages.map((page)=>(
<li className={
page === currentPage ? "page-item active" : "page-item"
}>
<p className="page-link"
onClick={()=>pagination(page)}
>{page}</p></li>
) )
}
</ul>
</nav>
</div>
</>
)
};
temporarily i am sending '7' (hard coded) to the api to check and it working fine. But i want clicked project id in this.
thank you!!!
You need only one id. You can get this id when you mapping projects in MyProjectList.js file at this line paginatedProject.map((user, _id) => ().
Just get projectid from user and then send it into onClick like this:
onCLick = {(e) => onRowClick(e, user.projectId)}
If I were you I would use react context for this situation because your issue is the meaning of using react context; React context is a general state that you can set or edit or use your general state in all your components easily

keep in memory a data of an API with React.js

I try to keep in mind by refreshing the page the price of a product. The default value of this price comes from data retrieved from an API and I would like with an input text to change this price and keep it at the refresh of the page. I tried with localStorage so it retrieves the new price in the console application but when I refresh the page the price remains the default one and not the new price put in the input
I leave you my code below and I thank you in advance for your explanations
ProductsDetails :
import React, { Component } from 'react'
import '../css/ProductsDetails.css'
import {AiOutlineArrowLeft} from "react-icons/ai";
import {Link} from 'react-router-dom'
export default class ProductsDetails extends Component {
constructor(props) {
super(props);
this.state = {
id: this.props.match.params.id,
price: 0
};
}
updatePrice = (e) => {
localStorage.setItem('price', e.target.value)
this.setState({
price: e.target.value
});
};
submitHandler = (e) => {
e.preventDefault();
const {
match: {
params: { id }
}
} = this.props;
this.props.updatePrice(id, this.state.price);
};
componentDidMount() {
const price = localStorage.getItem('price')
this.setState({price});
}
render() {
const {
match: {
params: { id }
},
products
} = this.props;
const listProduct = products.find((product) => product.id === Number(id))
if (!listProduct) return null;
return (
<div className="products__details">
<Link to="/">
<AiOutlineArrowLeft className="nav__arrow" />
</Link>
<h1 className="details__title">{listProduct.title}</h1>
<div className="details__align--desk">
<div className="details__img">
<img
className="product__img"
src={listProduct.image}
alt="Affichage du produit"
/>
</div>
<div className="products__align--desk">
<h2 className="product__title">Description</h2>
<p className="product__description">{listProduct.description}</p>
<h2 className="product__title">Price</h2>
<form className="form__price" onSubmit={this.submitHandler}>
<input
name="price"
className="input__price"
type="text"
defaultValue={Number(listProduct.price).toFixed(2)}
onChange={this.updatePrice}
/>
<p>
Price (including VAT):{" "}
{Number(listProduct.price * 1.2).toFixed(2)} €
</p>
<br />
<input
className="btn__update"
type="submit"
value="Update product"
/>
</form>
</div>
<div className="category__align--desk">
<h2 className="product__title">Category</h2>
<p className={`${listProduct.category==="men's clothing" ? "category__orange" : "category__green"} product__category`}>{listProduct.category}</p>
</div>
</div>
</div>
);
}
}
Products :
import React, { Component } from 'react';
import '../css/Products.css';
import './ProductsDetails'
import {Link} from 'react-router-dom'
export default class Products extends Component {
render() {
const listsProducts = this.props.products.map((listProduct) => {
return (
<tbody className="products__body" key={listProduct.id}>
<tr>
<td><Link to={{pathname: "/products-details/" + listProduct.id}}>{listProduct.title}</Link></td>
<td><p className={`${listProduct.category==="men's clothing" ? "category__orange" : "category__green"}`}>{listProduct.category}</p></td>
<td>{Number(listProduct.price).toFixed(2)}</td>
<td>
{Number(listProduct.price * 1.2).toFixed(2)} €
</td>
</tr>
</tbody>
);
});
return (
<main className="products">
<h1 className="products__title">Products management</h1>
<table cellSpacing="0">
<thead className="products__head">
<tr>
<th className="table--title">Product name</th>
<th className="table--title">Category</th>
<th className="table--title">Price</th>
<th className="table--title">Price (including VAT)</th>
</tr>
</thead>
{listsProducts}
</table>
</main>
);
}
}
App :
import React, { useState, useEffect } from 'react';
import './css/App.css';
import './css/Products.css';
import axios from 'axios';
import {Oval} from 'react-loading-icons'
import Navigation from './Components/Navigation';
import Products from './Components/Products';
import {BrowserRouter as Router, Route, Switch,} from 'react-router-dom';
import ProductsDetails from './Components/ProductsDetails';
export default function App() {
const [error, setError] = useState(null);
const [productsData, setProductsData] = useState([]);
const [isLoaded, setIsLoaded] = useState(false);
const updatePrice = (id, price) => {
setProductsData((productsData) =>
productsData.map((product) =>
product.id === Number(id)
? {
...product,
price: Number(price)
}
: product
)
);
};
useEffect(() => {
axios.get("https://fakestoreapi.com/products?limit=7").then((res) => {
setIsLoaded(true);
setProductsData(res.data);
},
(error) => {
setIsLoaded(true);
setError(error);
}
);
}, []);
if (error) {
return <div>Erreur : {error.message}</div>
} else if (!isLoaded) {
return <div>
<Oval stroke="#564AFF"/>
<p>Loading...</p>
</div>
} else {
return (
<div className="App">
<Router>
<Navigation/>
<Switch>
<Route
path="/products-details/:id"
render={(props) => (
<ProductsDetails
products={productsData}
updatePrice={updatePrice}
{...props}
/>
)}
/>
<Route path="/">
<Products products={productsData}
/>
</Route>
</Switch>
</Router>
</div>
)
}
}

React.createElement: type is invalid (redux/react-router?)

I realise this question has been asked quite a few times:
React.createElement: type is invalid -- expected a string
Element type is invalid: expected a string (for built-in components) or a class/function
Warning: React.createElement: type is invalid -- expected a string (for built-in components)
...but none of these have helped me. I get from these questions/answers that the general consensus is that this commonly happens with an import/export issue which (looking through my code base, is not the issue I have).
I have 3 components: CognitoLogin, ListGroups and InviteUserModal. CogntioLogin is an auth service and redirects to ListGroups using react-router-dom <Redirect>. This then renders InviteUserModal on the click of a button. This is my code (the bits that matter):
CognitoLogin.js
render() {
const { redirect } = this.state;
if (redirect) {
return <Redirect to={{
pathname: redirect,
state: {
email: this.state.email
}
}}/>;
}
return (
<div id="dialog-region">
<div className="login-view">
{ !this.props.location.state.loginAuth &&
<CognitoMessage email={this.state.email}/>
}
<div id="login-logo">
<Image id="pimberly-logo" src={PimberlyLogo} responsive/>
<Image id="cognito-logo" src={CognitoLogo} responsive/>
</div>
<LoginForm handleLogin={this.handleLogin}/>
<div className="checkbox forgottenReset">
<a id="reset-password" onClick={this.handlePasswordReset}>Forgotten password?</a>
</div>
<div className="checkbox forgottenReset">
<a id="resend-confirm" onClick={this.handleResend} href="#">Resend confirmation</a>
</div>
{ this.state.err &&
<ErrorAlert err={this.state.err} />
}
{ this.state.apiRes &&
<SuccessAlert res={this.state.apiRes} />
}
{ this.state.resend &&
<InfoAlert email={this.state.email}/>
}
</div>
</div>
);
}
ListGroups.js
import React, { Component } from "react";
import axios from "axios";
import { connect } from "react-redux";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { Table, ButtonToolbar, Button } from "react-bootstrap";
// Selectors
import { getUser } from "../selectors/user";
import { getGroupByName } from "../selectors/group";
// Actions
import { setUsersGroupsPersist } from "../actions/user";
// Components
import ErrorAlert from "./ErrorAlert";
import ListUsersModal from "./ListUsersModal";
import InviteUsersModal from "./InviteUsersModal";
// Styles
import "../../css/login.css";
class ListGroups extends Component {
constructor(props) {
super(props);
this.state = {
groups: [],
showModal: false,
groupName: "",
emailOfUser: props.location.state.email
};
this.handleRemoveInviteUsers = this.handleRemoveInviteUsers.bind(this);
this.closeModal = this.closeModal.bind(this);
this.props.setUsersGroupsPersist(this.state.emailOfUser);
}
handleRemoveInviteUsers(event) {
const groupSelected = this.props.groups.find((group) => {
return (group.name === event.currentTarget.dataset.groupName);
});
event.preventDefault();
this.setState({
groupAction: event.currentTarget.dataset.groupAction
});
return axios.post(`http://${process.env.BASE_API}/groups/users/list`, {
groupName: groupSelected.name
})
.then((res) => {
this.setState({
showModal: true,
users: res.data.Users
});
})
.catch((err) => {
this.setState({
apiErr: err
});
});
}
closeModal(event) {
this.setState({
showModal: false
});
}
render() {
const Modal = (this.state.groupAction === "remove") ? <ListUsersModal /> : <InviteUsersModal />;
return (
<>
<div id="dialog-region">
<Table striped bordered condensed hover>
<thead>
<tr>
<th scope="col" className="col-name">Name</th>
<th scope="col" className="col-description">Description</th>
<th scope="col" className="col-created">Created</th>
<th scope="col" className="col-buttons"></th>
</tr>
</thead>
<tbody>
{ this.props.groups.map(function(group, i) {
return <tr key={i}>
<td>{ group.name }</td>
<td>{ group.description }</td>
<td>{ group.created }</td>
<td>
<ButtonToolbar>
<Button bsStyle="primary" onClick={this.handleRemoveInviteUsers} data-group-name={group.name} data-group-action="invite">
<FontAwesomeIcon icon="plus"/> Invite users
</Button>
<Button bsStyle="danger" onClick={this.handleRemoveInviteUsers} data-group-name={group.name} data-group-action="remove">
<FontAwesomeIcon icon="trash"/> Remove users
</Button>
</ButtonToolbar>
</td>
</tr>;
}, this)}
</tbody>
</Table>
{ this.state.showModal &&
<Modal closeModal={this.closeModal}/>
}
{ this.state.apiErr &&
<ErrorAlert err={this.state.apiErr} />
}
</div>
</>
);
}
}
const mapStateToProps = (state, props) => {
const user = getUser(state, props.location.state.email);
const groups = user.groups.map((groupName) => {
return getGroupByName(state, groupName);
});
return {
user,
groups
};
};
export default connect(
mapStateToProps,
{ setUsersGroupsPersist }
)(ListGroups);
InviteUsersModal.js
import React, { Component } from "react";
import axios from "axios";
import { connect } from "react-redux";
import { Modal, Button, FormGroup, FormControl, ControlLabel, Table } from "react-bootstrap";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import ErrorAlert from "./ErrorAlert";
export default class InviteUsersModal extends Component {
constructor(props) {
super(props);
this.state = {
currentEmail: "",
emailsToInvite: []
};
this.handleInviteUser = this.handleInviteUser.bind(this);
this.handleEmailChange = this.handleEmailChange.bind(this);
}
handleInviteUsers(event) {
event.preventDefault();
return axios.post(`http://${process.env.BASE_API}/groups/users/invite`, {
emails: this.state.emailsToInvite,
emailOfInviter: this.props.emailOfUser,
groupName: this.props.groupName
})
.then((res) => {
this.props.closeModal();
})
.catch((err)=> {
this.setState({
apiErr: err
});
});
}
handleSubmit(event) {
this.setState({
emailsToInvite: [...this.state.emailsToInvite, this.state.currentEmail]
});
}
handleRemoveInvite(event) {
const emailToRemove = event.currentTarget.dataset.email;
event.preventDefault();
this.state.emailsToInvite.splice(this.state.emailsToInvite.indexOf(emailToRemove), 1);
}
handleEmailChange(event) {
this.setState({
currentEmail: event.currentTarget.value
});
}
handleModalClose() {
this.props.closeModal();
}
render() {
return (
<Modal show={this.props.showModal} onHide={this.handleModalClose} bsSize="large">
<Modal.Header closeButton>
<Modal.Title>Invite</Modal.Title>
</Modal.Header>
<Modal.Body>
<form role="form" onSubmit={this.handleSubmit}>
<FormGroup controlId="inviteUsersForm">
<FormControl type="email" value={this.state.currentEmail} placeholder="Email" onChange={this.handleEmailChange}></FormControl>
<Button type="submit" bsStyle="primary">
<FontAwesomeIcon icon="plus"/> Invite
</Button>
</FormGroup>
</form>
<hr/>
<h1>Users to invite</h1>
<Table striped bordered condensed hover>
<thead>
<tr>
<th scope="col">Email</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
{ this.state.emailsToInvite.map(function(email, i) {
return <tr key={i}>
<td>{ email }</td>
<td>
<Button bsStyle="danger" onClick={this.handleRemoveInvite} data-email={email}>
<FontAwesomeIcon icon="minus"/>
</Button>
</td>
</tr>;
}, this)}
</tbody>
</Table>
{ this.state.apiErr &&
<ErrorAlert err={this.state.apiErr}/>
}
</Modal.Body>
<Modal.Footer>
<Button onClick={this.handleModalClose}>Cancel</Button>
<Button onClick={this.handleInviteUsers} bsStype="primary">Save</Button>
</Modal.Footer>
</Modal>
);
}
}
As it looks, I am importing the component correctly. When debugging, the constructor is not even hit so am unsure where it is going wrong.
This did work before I introduced react-router/redux so is is possible they are the problem?
The error(s) happen when I click on the button to "Invite users". When debugging, I can get to the line to show the modal and any further than that will throw an error.
The full list of errors are:
EDIT
I have also tried rendering the InviteUsersModal via element variables instead so the render method would be:
render() {
const Modal = (this.state.groupAction === "remove") ? : ;
return (
<>
<div id="dialog-region">
<Table striped bordered condensed hover>
...
</Table>
{ this.state.showModal &&
{Modal}
}
...
</div>
</>
);
}
but get similar errors:
I think you should adjust the following line:
const Modal = (this.state.groupAction === "remove") ? <ListUsersModal /> : <InviteUsersModal />;
To:
const Modal = (this.state.groupAction === "remove") ? ListUsersModal : InviteUsersModal;
You are assigning an instance of the component to variable Modal and instead of rendering it you are using it as Component

Categories

Resources