I've a problem with my react app.The navbar worked correctly before the ItemDetailContainer, and i want to show in the DOM an specific product. When adding the ItemDetailContainer to app.js, the page doesn't show anything (including the navbar) . Here's my code:
ItemDetailContainer.jsx:
const {products} = require('../utils/data');
const ItemDetailContainer = () => {
const [arrayList, SetArrayList] = useState({});
useEffect(() => {
customFetch(2000, products[0])
.then(result => SetArrayList(result))
.catch(err => console.log(err))
}, [])
return (
<div>
<ItemDetail products={arrayList}/>
</div>
);
}
here's my array in data.js that i want to access:
const products = [
{
id: 1,
image: "https://baltimore.com.ar/img/articulos/4188.png",
title: "Vino Abras Malbec 750cc",
price: 2.500,
stock: 8,
initial: 1
}
ItemDetail.jsx:
const ItemDetail = ({product}) => {
return(
<>
{product.image}
{product.title}
</>
)
}
CustomFetch.js:
let is_ok = true
let customFetch = (time, array) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (is_ok) {
resolve(array)
} else {
reject("error")
}
}, time)
})
}
and my app.js:
function App() {
return (
<div>
<Navbar/>
<ItemDetailContainer/>
</div>
);
}
I've done all the imports and exports, but I dont' know what's the problem.
Since you are passing a product prop in <ItemDetail product={arrayList}/> within ItemDetailContainer make sure you destructure your props correctly in ItemDetail component,like so :
const ItemDetail = ({product}) => {
return(
<>
{product.image}
{product.title}
</>
)
}
Which is basically like this :
const ItemDetail = (props) => {
return (
<>
{props.product.image}
{props.product.title}
</>
);
};
check it out in code sandbox
Related
I have two components.
First is called: BucketTabs
Second is called:BucketForms
To have a better idea. Below pictures illustrate it.
When I switching tab, different form will be showed below.
Q: Whenever I switch from one tab to other tab, and then switch back, the content in the previous BucketForms will be gone. But, gone data are supposed to be stored into a state of that BucketForms.
In fact, I've memo the BucketForms already, so I've expected the content(data) would not be gone.
What's the problem and how could I prevent the data to be gone after switching tab.
My BucketTabs:
import { BucketForms } from '~components/BucketForms/BuckForms'
export const BucketTabs: React.FC = () => {
const items = useMemo<ContentTabsItem[]>((): ContentTabsItem[] => {
return [
{
title: '1',
renderContent: () => <BucketForms key="1" bucketCategory="1" />,
},
{
title: '2',
renderContent: () => <BucketForms key="2" bucketCategory="2" />,
},
]
}, [])
return (
<div className="row">
<div className="col">
<ContentTabs items={tabs} kind="tabs" />
</div>
</div>
)
}
BucketForms
function PropsAreEqual(prev, next) {
const result = prev.bucketCategory === next.bucketCategory;
return result;
}
interface IData {
portfolioValue?: number
}
export const BucketForms: React.FC<IProps> = React.memo(props => {
const { bucketCategory } = props
const [data, setData] = useState<IData>({
})
const view = ({
portfolioValue,
}: IData) => {
return (
<>
<div className="row portfolio">
<FormNumericInput
key="input-portfolio-value"
name="portfolioValue"
required
value={portfolioValue}
/>
</div>
</>
)
}
return (
<Form
onChange={e => {
setData({ ...data, ...e, })
}}
>
{view(data)}
</Form>
)
}, PropsAreEqual)
I have three componente
ComponentFather ( grand Parent component )
ComponentChild ( Child component of ComponentFather)
ComponentChildChild ( Child component of ComponentChildChild)
The three components are connected so that each component is this child above.
I props data from ComponentFather to ComponentChildChild. And that work. But when I try to pring all data no work. Print only first data Chek code ->
export const ComponentFather = () => {
// this is only mockup data for example
let data = [
{
id: 2000004,
name: 'test123.pdf',
},
{
id: 2000005,
name: 'test123123.pdf',
},
];
return (
<ComponentChild data={data}>
</ComponentChild>
);
};
export const ComponentChild = ({data}) => {
// console.log(data); i got all data.
return (
{data.map((singleData) => {
<ComponentChildChild data={data}>
</ComponentChildChild>
})}
);
};
export const ComponentChildChild = ({data}) => {
return (
<div> { data.name } </div>
<div> { data.id } </div>
);
};
Code above no work......
Why?
What i am try also to set data.map inside ComponentChildChild component
export const ComponentChildChild = ({data}) => {
return (
{ data.map((singleData) => {
<div> { data.name } </div>
<div> { data.id } </div>
})}
);
};
Also no work. Work only when I hardcode example data[0].name
export const ComponentChildChild = ({data}) => {
return (
<div> { data[0].name } </div>
<div> { data[0].id } </div>
);
};
But sometimes i will have 10 items... I need to print each
pass singleData instead data
export const ComponentChild = ({data}) => {
return (
{data.map((singleData) => {
<ComponentChildChild data={singleData}>
</ComponentChildChild>
})}
);
};
I have connected to an api and have pulled some data into my project with the name of 'data'. This data is being rendered dynamically into a card component. I am now trying to arrange the order from highest price to lowest price on the click of a button with useState but cannot figure it out. Below is what i have so far:
import React, { useState } from "react";
import "./App.scss";
import { useQuery } from "#apollo/react-hooks";
import GET_PRODUCTS_IN_COLLECTION from "./gql/getCollection";
import ProductCard from "./components/ProductCard/ProductCard";
const App = (props) => {
const { data, loading, error } = useQuery(GET_PRODUCTS_IN_COLLECTION, {
variables: {
count: 10,
handle: "skateboard",
},
});
// console.log(data)
const [reversed, setReversed] = useState(false);
const [highLow, setHighLow] = useState(false);
const [lowHigh, setLowHigh] = useState(false);
const [remove, setRemove] = useState(false);
const reverseOrder = () => {
setReversed(!reversed);
};
const highToLow = () => {
setHighLow(!highLow);
};
const lowToHigh = () => {
setLowHigh(!lowHigh);
};
const removeLast = () => {
setRemove(!remove);
};
if (loading) {
// Data is still loading....
return <div className="App">Loading....</div>;
}
return (
<div className="App">
<header className="App-header"></header>
<main>
<div className="buttonGroup">
<button onClick={reverseOrder}>Reverse Product Order</button>
<button onClick={highToLow}>Price High to Low</button>
<button onClick={lowToHigh}>Price Low to High</button>
<button onClick={removeLast}>Display 9 products</button>
</div>
{/*
Your render components go here
*/}
<div className="ProductList">
{reversed
? data.collectionByHandle.products.edges
.slice()
.reverse()
.map((product) => {
return <ProductCard productData={product} />;
})
: highLow
? data.collectionByHandle.products.edges
.slice()
.sort((a,b) => (a.node.vendor - b.node.vendor))
.map((product) => {
return <ProductCard productData={product} />;
})
: lowHigh
? data.collectionByHandle.products.edges
.slice()
.map((product) => {
return <ProductCard productData={product} />;
})
.splice(1)
: remove
? data.collectionByHandle.products.edges
.slice()
.map((product) => {
return <ProductCard productData={product} />;
})
.splice(1)
: data.collectionByHandle.products.edges.map((product) => {
return <ProductCard productData={product} />;
})}
</div>
</main>
</div>
);
};
export default App;
image of array
You can change your code like the following example:
Some points to keep in mind :
Try to avoid if statment in JSX .
Put your events in seprated functions to make it easy for you to manage .
import React, { useState ,useEffect} from "react";
import "./App.scss";
import { useQuery } from "#apollo/react-hooks";
import GET_PRODUCTS_IN_COLLECTION from "./gql/getCollection";
import ProductCard from "./components/ProductCard/ProductCard";
const App = (props) => {
const [myData, setMyData] = useState(data);
useEffect (() => {
const { data, loading, error } = useQuery(GET_PRODUCTS_IN_COLLECTION,
{
variables: {
count: 10,
handle: "skateboard",
},
});
setMyData(data);
},[]);
const reverseOrder = () => {
let newData = myData.reverse();
setMyData([...newData]);
};
const highToLow = () => {
let newData = myData.sort((a, b) => b.node.vendor- a.node.vendor);
setMyData([...newData]);
};
const lowToHigh = () => {
let newData = myData.sort((a, b) => a.node.vendor- b.node.vendor);
setMyData([...newData]);
};
const removeLast = () => {
myData.splice(-1, 1);
setMyData([...myData]);
};
if (loading) {
// Data is still loading....
return <div className="App">Loading....</div>;
}
return (
<div className="App">
<header className="App-header"></header>
<main>
<div className="buttonGroup">
<button onClick={reverseOrder}>Reverse Product Order</button>
<button onClick={highToLow}>Price High to Low</button>
<button onClick={lowToHigh}>Price Low to High</button>
<button onClick={removeLast}>Display 9 products</button>
</div>
{
myData.map((product) => {
return <ProductCard productData={product} />;
});
}
</div>
</main>
</div>
);
};
export default App;
Assuming the values are alphanumerical javascript has built in function "sort" to do that. Even if they are not numerical there has to be a way to read their value that you can use!
Then its pretty straight forward (modified from w3schools):
const fruits = [2,1,"Banana", "Orange", "Apple", "Mango"];
fruits.sort();
will create array [1,2,Apple,Banana,Mango,Orange]
You should be able to do something along these lines in your program.
(just droping: if you want to reverse the order simply use reverse() method on array)
I don't know what your data looks like but this should work.
https://www.w3schools.com/js/js_array_sort.asp
I am building a to-do/notes app in order to learn the basics of Redux, using React hooks and Typescript.
A note is composed of an ID and a value. The user can add, delete or edit a note.
The add / delete mechanics work fine. But the edit one is trickier for me, as I'm questionning how it should be implemented.
I think my reducer's code is fine. The problem lies between my component (Note.tsx) and its parent one (App.tsx).
When i'm logging the value, I can see that the new updated/edited value of the note is not sent to the reducer. As a result, my note is not edited with the new value.
I've tried "cloning" the redux store and making my changes here, but it seems tedious and unnatural to me. Should I just call the edit method from my Note.tsx component ?
Is there a clean / conventional way to do this ?
Here is my code :
App.tsx
function App() {
const notes = useSelector<NotesStates, NotesStates['notes']>(((state) => state.notes));
const dispatch = useDispatch();
const onAddNote = (note: string) => {
dispatch(addNote(note));
};
const onDeleteNote = (note: NoteType) => {
dispatch(deleteNote(note));
};
const onEditNote = (note: NoteType) => {
dispatch(updateNote(note));
};
return (
<div className="home">
<NewNoteInput addNote={onAddNote} />
<hr />
<ul className="notes">
{notes.map((note) => (
<Note
updateNote={() => onEditNote(note)}
deleteNote={() => onDeleteNote(note)}
note={note}
/>
))}
</ul>
</div>
);
}
Note.tsx
interface NoteProps {
deleteNote(): void
updateNote(noteValue: string | number): void
note: NoteType
}
const Note: React.FC<NoteProps> = ({ deleteNote, updateNote, note: { id, value } }) => {
const [isEditing, setIsEditing] = useState(false);
const [newNoteValue, setNewNoteValue] = useState(value);
const onDeleteNote = () => {
deleteNote();
};
const onUpdateNote = () => {
updateNote(newNoteValue);
setIsEditing(false);
};
const handleOnDoubleClick = () => {
setIsEditing(true);
};
const renderBody = () => {
if (!isEditing) {
return (
<>
{!value && <span className="empty-text">Note is empty</span>}
<span>{value}</span>
</>
);
}
return (
<input
value={newNoteValue}
onChange={(e) => setNewNoteValue(e.target.value)}
onBlur={onUpdateNote}
/>
);
};
return (
<li className="note" key={id}>
<span className="note__title">
Note n°
{id}
</span>
<div className="note__body" onDoubleClick={handleOnDoubleClick}>
{renderBody()}
</div>
<button type="button" onClick={onDeleteNote}>Delete</button>
</li>
);
};
export default Note;
and the notesReducer.tsx
export interface NotesStates {
notes: Note[]
}
export interface Note {
id: number
value: string
}
const initialState = {
notes: [],
};
let noteID = 0;
export const notesReducer = (state: NotesStates = initialState, action: NoteAction): NotesStates => {
switch (action.type) {
case 'ADD_NOTE': {
noteID += 1;
return {
...state,
notes: [...state.notes, {
id: noteID,
value: action.payload,
}],
};
}
case 'UPDATE_NOTE': {
return {
...state,
notes: state.notes.map((note) => {
if (note.id === action.payload.id) {
return {
...note,
value: action.payload.value,
};
}
return note;
}),
};
}
case 'DELETE_NOTE': {
return {
...state,
notes: [...state.notes
.filter((note) => note.id !== action.payload.id)],
};
}
default:
return state;
}
};
Thanks to #secan in the comments I made this work, plus some changes.
In App.tsx :
<Note
updateNote={onEditNote}
deleteNote={() => onDeleteNote(note)}
note={note}
/>
In Note.tsx :
interface NoteProps {
deleteNote(): void
updateNote(newNote: NoteType): void // updated the signature
note: NoteType
}
// Now passing entire object instead of just the value
const onUpdateNote = (newNote: NoteType) => {
updateNote(newNote);
setIsEditing(false);
};
const renderBody = () => {
if (!isEditing) {
return (
<>
{!value && <span className="empty-text">Note is empty</span>}
<span>{value}</span>
</>
);
}
return (
<input
value={newNoteValue}
onChange={(e) => setNewNoteValue(e.target.value)}
// modifying current note with updated value
onBlur={() => onUpdateNote({ id, value: newNoteValue })}
/>
);
};
Im in the process of converting my old class components to functional with react hooks. Doing so, when rendering my header component, it's throwing a Minified React error #185 error. Console logging the output shows that it is continuously looping over the data which each being logged to the console multiple times.
Previously when this was done with class style components there were no issues. Im not sure what I am overlooking.
MainLayout Component
const MainLayout = () => {
const [payload, setPayload] = useState({});
const [isInit, setisInit] = useState(false);
useEffect(() => {
payloadApi().then((res) => {
setPayload(res.data);
setisInit(true);
}).catch(err => {
console.log('err payload')
});
}, []);
return (
<div className="container">
{isInit && (
<Header
payload={payload}
activeUser="bob"
/>
)}
</div>
);
};
Header Component
const Header = ({payload, activeUser}) => {
const renderUserList = () => {
const filteredUsers = payload.users.filter(function (e) {
return e.user !== activeUser;
});
return filteredUsers.map((k, i) => {
console.log(k.user);
return (
<li key={k.user}>
<span>{k.user}</span>
</li>
)
});
};
return (
<header>
<ul>
{renderUserList()}
</ul
</header>
);
};
Payload
{
users: [
{
user: "joe",
},
{
user: "bob",
},
{
user: "sally",
}
]
}
The only thing I can spot that might be responsible is the two set calls (payload and init), which you don't need anyway. Initialise payload with null, and as long it's null don't render the Header:
const MainLayout = ({RouteComponent}) => {
const [payload, setPayload] = useState(null);
useEffect(() => {
payloadApi().then((res) => {
setPayload(res.data);
}).catch(err => {
console.log('err payload')
});
}, []);
return (
<div className="container">
{payload && (
<Header
payload={payload}
activeUser="bob"
/>
)}
</div>
);
};
You can also prevent needless calls to renderUserList with useMemo:
const Header = ({payload, activeUser}) => {
const userList = useMemo(() => {
const filteredUsers = payload.users.filter(function (e) {
return e.user !== activeUser;
});
return filteredUsers.map((k, i) => {
console.log(k.user);
return (
<li key={k.user}>
<span>{k.user}</span>
</li>
)
});
}, [payload, activeUser]);
return (
<header>
<ul>
{userList}
</ul>
</header>
);
};