I am looking for feedback regarding this React code I am writing. I previously posted when I first started out on this and I have since iterated on it and tried to implement best practices. I am learning a lot and I cannot help, but feel that my current code is clunky. In what ways can I improve the design?
I am having a hard time figuring out what needs to be a component, what needs to be a function, and what can just be a value (even then whether it should be state). I also feel like I am misusing variable scope, but I can't put my finger on how. One specific thing I cannot resolve is ESlint is highlighting mostly everything in my file with this error "Function component is not a function declaration". Any and all feedback is appreciated. Thank you!
/* eslint-disable react/prop-types */
/* eslint-disable no-underscore-dangle */
import axios from 'axios';
import React, { useState, useEffect, useCallback, useRef } from 'react';
import { GetWishes, InsertWish } from './apis';
// Main component, acts a wrapper for the entire screen content
const Wishlist = () => {
const [loading, setLoading] = useState('initial');
const [listOfWishes, setListOfWishes] = useState('default');
// Passed down to update the main list state
function updateListOfWishes(list) {
setListOfWishes([...list]);
}
// Sorting lists dynamically
function dynamicSort(property) {
let sortOrder = 1;
let newprop = '';
if (property[0] === '-') {
sortOrder = -1;
newprop = property.substr(1);
}
return (a, b) => {
let result = 0;
if ((a[newprop] < b[newprop])) {
result = -1;
} else if (a[newprop] > b[newprop]) {
result = 1;
}
return result * sortOrder;
};
}
// Get all items from DB, this is main list
async function GetWishesList() {
try {
const apiresp = await GetWishes();
apiresp.sort(dynamicSort('-source'));
const goodlist = apiresp.map((item) => ({ ...item, isReadOnly: true, show: true }));
setListOfWishes(goodlist);
setLoading('false');
} catch (e) {
console.log(`Error in Wishlist.GetWishesList: ${e.message}`);
}
}
// Only once, get items and set loading state
useEffect(() => {
setLoading('true');
GetWishesList();
}, []);
if (loading === 'initial') {
return <h2 className="content">Initializing...</h2>;
}
if (loading === 'true') {
return <h2 className="content">Loading...</h2>;
}
// Return header and content, pass down function for deep state update
return (
<div className="contentwrapper">
<WishlistHeader fullList={listOfWishes} updateListOfWishes={updateListOfWishes} />
<WishTable fullList={listOfWishes} updateListOfWishes={updateListOfWishes} />
</div>
);
}
// Header component
const WishlistHeader = (props) => {
const [filter, setFilter] = useState('all');
let list = props.fullList;
// Get length of current filtered list
function getShowCount(list) {
return list.filter((item) => item.show === true).length;
}
// Update shown list items when filter changes
const HandleFilterChange = (e) => {
const { fullList, updateListOfWishes } = props;
const { value } = e.target;
for (let i = fullList.length - 1; i >= 0; i -= 1) {
fullList[i].isReadOnly = true;
fullList[i].show = true;
if (value !== 'all' && fullList[i].category !== value) {
fullList[i].show = false;
}
}
setFilter(value);
const newlist = fullList.map(i => {
return { ...i };
});
updateListOfWishes(newlist);
}
// Return header component content
return (
<div className="contentBanner">
<h1 className="wishTitle">
Wishes:
{' '}
{getShowCount(list)}
</h1>
<label htmlFor="category">
<p className="bannerFilter">Category</p>
<select id="category" name="category" value={filter} onChange={(e) => HandleFilterChange(e)}>
<option value="all">All</option>
<option value="default">Default</option>
<option value="camping">Camping</option>
<option value="hendrix">Hendrix</option>
<option value="decor">Decor</option>
</select>
</label>
</div>
);
}
// Component to show list of items
function WishTable(props) {
const rows = [];
let { fullList, updateListOfWishes } = props;
if (fullList === null) {
console.log('currentList is null');
} else {
fullList.forEach(function (item) {
rows.push(
<div key={item._id} >
<WishRow item={item} currentList={fullList} updateListOfWishes={updateListOfWishes} />
</div>)
})
}
return (
<div className="content">
{rows}
</div>
);
};
// Individual row render for each item
const WishRow = (props) => {
let item = props.item;
let prevItem = useRef(item);
// Store unedited item in case of cancel, mark not read only
const handleEdit = () => {
let { item, currentList, updateListOfWishes } = props;
prevItem.current = { ...item };
const newlist = currentList.map(i => {
if (i._id === item._id) {
return { ...i, isReadOnly: false }
}
return { ...i };
});
updateListOfWishes(newlist);
};
// Send item to DB
async function insertWish(item) {
try {
await InsertWish(item);
} catch (e) {
console.log(`Error in wishlist.insertWish: ${e.message}`);
}
};
// Send current item info to DB and mark read only
const handleSubmit = () => {
let { item, currentList, updateListOfWishes } = props;
insertWish(item);
const newlist = currentList.map(i => {
if (i._id === item._id) {
return { ...i, isReadOnly: true };
}
return { ...i };
});
updateListOfWishes(newlist);
};
// Return content for submit button
function Submit(item) {
if (!item.isReadOnly) {
return (
<span>
<button className="typicalbutton" type="button" onClick={() => handleSubmit(item)}>
Submit
</button>
</span>
);
}
return null;
};
// Return content for edit button
function ShowEdit(item) {
if (item.source === 'manual') {
return (
<span >
<button className="typicalbutton righthand" type="button" onClick={() => handleEdit(item, props.currentList)}>
Edit
</button>
</span>
);
}
return null;
};
// Revert to unedited item and mark read only
const handleCancel = () => {
let { item, currentList, updateListOfWishes } = props;
const newlist = currentList.map(i => {
if (i._id === item._id) {
return { ...prevItem.current, isReadOnly: true }
}
return { ...i };
});
updateListOfWishes(newlist);
};
// Return content for cancel button
function Cancel(item) {
if (!item.isReadOnly) {
return (
<span>
<button className="typicalbutton" type="button" onClick={(e) => handleCancel(e, prevItem, item)}>
Cancel
</button>
</span>
);
}
return null;
};
// Update item when fields edited
const handleChange = (e) => {
let { item, currentList, updateListOfWishes } = props;
const { name, value } = e.target;
item[name] = value;
const newlist = currentList.map(i => {
return { ...i };
});
updateListOfWishes(newlist);
};
// Update item for category change
const handleCategoryChange = (e) => {
const { value } = e.target;
let { item, currentList, updateListOfWishes } = props;
item.category = value;
const newlist = currentList.map(i => {
return { ...i };
});
updateListOfWishes(newlist);
};
// Open url in new tab
const openInTab = (url) => {
const newWindow = window.open(url, '_blank', 'noopener,noreferrer');
if (newWindow) newWindow.opener = null;
};
// Get the url from backend
async function getUrl(link) {
const toflask = `/go_outside_flask/${link}`;
const tempurl = await axios.get(toflask);
const newurl = tempurl.data;
return newurl;
}
// Get the url from backend and go to it in new tab
const goToLink = async (link) => {
let taburl = '';
try {
taburl = await getUrl(link);
return openInTab(taburl);
} catch (e) {
console.log(`Error getting url from link: ${link} ${e.message}`);
return window.location.href;
}
};
// Row content, if read only show just fields, if not read only then show different buttons and editable fields
return (
<div >
{item.show ? (
<div className="wish">
<div className="wishatt">
{
item.isReadOnly ? (
<div>
<span className="wishatt capital">
Category:
{item.category}
</span>
{ShowEdit(item)}
<div className="wishatt">
Item Name:
{item.name}
</div>
<div className="wishatt">
Description:
{item.description}
</div>
<div className="wishatt">
Cost:
{item.cost}
</div>
<span>Link: </span>
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
<a className="wishatt" href="#" onClick={(e) => goToLink(item.link)}>{item.link}</a>
<div className="wishatt">
Quantity:
{item.quantity}
</div>
</div>
)
: (
<span>
<label htmlFor="category">
Category:
<select name="category" onChange={(e) => handleCategoryChange(e, item)} value={item.category}>
<option value="default">Default</option>
<option value="camping">Camping</option>
<option value="hendrix">Hendrix</option>
<option value="decor">Decor</option>
</select>
</label>
<span className="righthandSection">
{Submit(item)}
{Cancel(item)}
</span>
<div>
<div>
<label htmlFor="name">
Item Name:
</label>
<input className="wishatt" name="name" placeholder="Name" onChange={(e) => handleChange(e, item)} value={item.name} />
</div>
<div>
<label htmlFor="description">
Description:
</label>
<input className="wishatt" name="description" placeholder="Description" onChange={(e) => handleChange(e, item)} value={item.description} />
</div>
<div>
<label htmlFor="cost">
Cost:
</label>
<input className="wishatt" name="cost" placeholder="Cost" onChange={(e) => handleChange(e, item)} value={item.cost} />
</div>
<div>
<label htmlFor="link">
Link:
</label>
<input className="wishatt" name="link" placeholder="Link" onChange={(e) => handleChange(e, item)} value={item.link} />
</div>
<div>
<label htmlFor="quantity">
Quantity:
</label>
<input className="wishatt" name="quantity" placeholder="Quantity" onChange={(e) => handleChange(e, item)} value={item.quantity} />
</div>
<div className="wishatt">
Wishlist:
{item.wishlist}
</div>
</div>
</span>
)
}
</div>
</div>
) : null}
</div>
)
}
export default Wishlist;
Aucun commentaire:
Enregistrer un commentaire