I write a React.js note web application where a user can add up to 10 notes.
I use map() to iterate the array of notes, and a useState(1) hook to update its count (the default number of notes is 1), so I would like to do something like this:
{[...Array(noteCount)].map((_, i) => <Note onUpdateNoteCount={() =>setNoteCount(n => n - 1)} key={i} />)}
The thing is that the Note() component is inside a Main() component which is in the App() component, so I want to get the needed values as props of App(), and than use them in Note(), but can not figure out how and where to put it.
Thanks!
App.js
import React from 'react';
import Header from './Header';
import Main from './Main';
function App () {
const [noteCount, setNoteCount] = React.useState(1);
function multiplyNoteComponent () {
if (noteCount < 20) {
setNoteCount(n => n + 1)
}
else {
alert('too many notes. remove or combine some of them together!')
}
}
return (
<div>
<Header/>
{[...Array(noteCount)].map((_, i) => <Main onUpdateNoteCount={() =>setNoteCount(n => n - 1)} key={i} />)}
<button
style={{left: '5%'}}
id='addNoteBtn'
onClick={multiplyNoteComponent}
title='Add a note'
>
+
</button>
</div>
);
}
export default App;
Main.js
import React from 'react';
import Note from './Note';
function Main () {
return (
<main>
your notes are:
<Note/>
</main>
)
}
export default Main;
Note.js
import React from 'react';
function Note () {
return (
<div> <button title='delete note' onClick={}>X</delete>
<li>
<input type='text'/>
</li>
</div>
)
}
export default Note
Edit: the reason I think I need the setNoteCount() function to be used in the Note() component, is for the count down when a note is being deleted (every note has its own delete button).
I would recommend this architecture of the your App.
Store the Notes array at the App level.
Add a note using NoteInput which adds a notes to your Notes array.
Map your Notes using the Note component which takes onDelete as a prop from App level.
Your App component should be responsible for storing and delete a note from the state.
In your example, notesCount is meant to a derivative state.
i.e it could be derived simply from the Notes array (notes.length).
So, rather than storing notesCount, I recommend storing notes and deriving count from it.
You could see the working example here :- https://stackblitz.com/edit/react-g19tei
import React from "react";
import "./style.css";
const NOTES_ALLOWED = 10;
export default function App() {
const [notes, setNotes] = React.useState([]);
function addNote(newNote) {
if (notes.length === NOTES_ALLOWED) {
alert(`Only ${NOTES_ALLOWED} notes are allowed to be added`)
} else {
setNotes([...notes, newNote]);
}
}
function handleDelete(deleteNoteIdx) {
const newNotes = [...notes];
// delete the note at the specific index
newNotes.splice(deleteNoteIdx, 1)
setNotes(newNotes);
}
return (
<div>
<div style={{ marginTop: 20, marginBottom: 20 }}>
<p>Your notes are</p>
{notes.map((note, idx) => (
<Note
note={note}
onDelete={() => handleDelete(idx)}
/>
))}
</div>
<NoteInput onAdd={addNote} />
</div>
);
}
function Note({ note, onDelete }) {
return (
<div>
<p>{note}
<button onClick={onDelete}>Delete Note</button>
</p>
</div>
)
}
function NoteInput({ onAdd }) {
const [note, setNote] = React.useState('');
function handleSubmit(e) {
e.preventDefault();
const noteToBeSend = note;
setNote('')
onAdd(noteToBeSend.trim());
}
return (
<div>
<form onSubmit={handleSubmit}>
<input
type="text"
value={note}
onChange={e => setNote(e.target.value)}
required
/>
<button type="submit">Add Note</button>
</form>
</div>
)
}
Related
I'm doing a broker simulator and I need to pass the data (side,price,volume,timestamp) into different tab, which is like grandparent's brother. The problem is I have no idea, how to transport data that far
Modalbuy.js
import React from "react";
import "./modal.css";
const Modalbuy = ({active, setActive,price}) => {
return (
<div className={active ? "modal active" : "modal"} onClick={() => setActive(false)}>
<div className="modal__content" onClick={e => e.stopPropagation()}>
<header>Make order</header>
<p>BUY {price}</p>
<input placeholder="Volume">{volume}</input>
<div>
<button onClick={() =>{this.props.addData("BUY", price, volume,)}}>Ok</button>
<button>Cancel</button>
</div>
</div>
</div>
)
}
export default Modalbuy;
Select.js
import React, {useState} from "react";
import './select.css'
import Modalbuy from "../popup/Modalbuy"
import Modalsell from "../popup/Modalsell"
import { useEffect } from "react";
const Select = () => {
const [value, setValue] = useState("");
const [usdrub, setUsdrub] = useState(Math.random() * (64-61) + 61);
useEffect(() => {
const interval = setInterval(() => {
setUsdrub(Math.random() * (64-61) + 61);
}, 10000);
return () => clearInterval(interval);
}, [])
const [rubusd, setRubusd] = useState(Math.random() * 2);
useEffect(() => {
const interval = setInterval(() => {
setRubusd(Math.random() * 2);
}, 10000);
return () => clearInterval(interval);
}, [])
function changeSelect(event) {
setValue(event.target.value)
}
const [modalBuyActive, setModalBuyActive] = useState(false)
const [modalSellActive, setModalSellActive] = useState(false)
function addData(side, price, volume, timestamp) {
this.setState({side:side, price:price, volume:volume, timestamp:timestamp})
}
return (
<div>
<select value = {value} onChange={changeSelect}>
<option>Choose instrument</option>
<option name="USD/RUB" value={usdrub}>USD/RUB</option>
<option name="RUB/USD" value={rubusd}>RUB/USD</option>
</select>
<div className="Curr">
<div className="Buy" name="buy"> <button className="Buy" type="btn" onClick={() => setModalBuyActive(true)}>BUY {value + 1} </button>
</div>
<div className="Sell" name="sell"><button className="Sell" type="btn" onClick={() => setModalSellActive(true)}>SELL {value}</button></div>
</div>
<Modalbuy active={modalBuyActive} setActive={setModalBuyActive} price={value + 1} addData={addData}/>
<Modalsell active={modalSellActive} setActive={setModalSellActive} price={value}/>
</div>
)
}
export default Select
Trading.js
import React from "react";
import Timer from "./Timer";
import Select from "./select_curr/Select";
const Trading = () => {
return (
<div>
<Timer/>
<Select/>
</div>
)
}
export default Trading;
Page.js
import React from 'react'
import { Tabs, TabList, TabPanel, Tab } from 'react-re-super-tabs'
import CustomTab from './customTab'
import Trading from '../trading/Trading.js'
import Table from '../archive/table'
const Page = () => {
return (
<div>
<Tabs activeTab='about'>
<TabList>
<Tab component={CustomTab} label='Trading' id='trading' />
<Tab component={CustomTab} label='Archive' id='archive' />
</TabList>
<TabList>
<TabPanel component={Trading} id='trading' />
<TabPanel component={Table} id='table' />
</TabList>
</Tabs>
</div>
)
}
export default Page;
So I need to make a table in Table.js and import there data I got from module dialogue
To summarize the way I understand your problem, The component Modalbuy need to share data with your component Table.
Here are some way I can advise you.
1. The common Parent manage the state
const Page = () => {
const [yourData, setYourData] = useState([])
return (
[...]
<TabPanel component={props=>(<Trading {...props} value={yourData} onChange={setYourData} />)} id='trading' />
<TabPanel component={props=>(<Table {...props} value={yourData} onChange={setYourData} />)} id='table' />
[...]
)
}
This implies intermediate component are in charge to follow those new props to Modalbuy
2. Using Redux librairy
Redux is basicly a librairy to help you to define a global state for your app. You define actions your components can call to transform this global state. On the other way, components can subscribe to that state to be refreshed when a change happen.
Don't hesitate to have a look to tutorials https://react-redux.js.org/tutorials/quick-start
3. Using React Context
export const YourDataContext = React.createContext({})
const Page = () => {
const [yourData, setYourData] = useState([])
return (
<YourDataContext.Provider value={{yourData, setYourData}}>
[...]
</YourDataContext.Provider>
)
}
const Modalbuy = ({active, setActive,price}) => {
const { yourData, setYourData } = React.useContext(YourDataContext)
[...]
})
You define a context YourDataContext. You share a value to this context, here we create a state and passing the getter/setter. All child of the Provider can access the attribute value with the hook React.useContext(YourDataContext)
I want to dynamically add component every time a button is clicked. But I can't figure it out myself, and most answers I found online are class based.
This is what I wrote: I want to pass a component and a name props every time the button is clicked.
import List from "./List";
function App() {
let listCount = 0;
const extraList = () => {
listCount++;
return(
<List name={`list-{listCount}`} />
);
};
return (
<div>
<button onClick={extraList}>Add List</button>
</div>
);
}
export default App;
You can use useState
function App() {
const [listCount, setListCount] = useState(0);
const extraList = () => {
setListCount(listCount + 1);
};
return (
<div>
{Array.from({ length: listCount }, (_, i) => i + 1).map((i) => (
<List name={`list-${i}`} />
))}
<button onClick={extraList}>Add List</button>
</div>
);
}
This is a beginner react question as I am learning.
I have two files hero.js and sidebar.js. There are two functions I have in hero.js that need to be accessed from buttons in sidebar.js (namely: onSave and onRestore).
How do I pass those functions down. I realize I probably need to do something like `this.onSave' but not sure how as still new to react.
hero.js
import React, { useState, useRef, useCallback } from "react";
import ReactFlow, {removeElements, addEdge,Controls, updateEdge, ReactFlowProvider, useZoomPanHelper,} from "react-flow-renderer";
import Sidebar from './sidebar';
import localforage from 'localforage';
localforage.config({
name: 'react-flow-docs',
storeName: 'flows',
});
...
const { transform } = useZoomPanHelper();
const onSave = useCallback(() => {
if (rfInstance) {
const flow = rfInstance.toObject();
localforage.setItem(flowKey, flow);
}
}, [rfInstance]);
const onRestore = useCallback(() => {
const restoreFlow = async () => {
const flow = await localforage.getItem(flowKey);
if (flow) {
const [x = 0, y = 0] = flow.position;
setElements(flow.elements || []);
transform({ x, y, zoom: flow.zoom || 0 });
}
};
restoreFlow();
}, [setElements, transform]);
return(
<div className="hero_container" >
<ReactFlowProvider>
<div className='rowC'>
<div className='reactflow-wrapper' style={{ height: 500, width: 1200 }} ref={reactFlowWrapper} >
<ReactFlow
elements={elements}
onLoad={onLoad}
onElementsRemove={onElementsRemove}
onEdgeUpdate={onEdgeUpdate}
onConnect={onConnect}
onDragOver={onDragOver}
onDrop={onDrop}
deleteKeyCode={46} /* 'delete'-key */
>
<Controls />
</ReactFlow>
</div>
<div>
<Sidebar />
</div>
</div>
</ReactFlowProvider>
</div>
);
};
sidebar.js
mport React from 'react';
const Sidebar = () => {
const onDragStart = (event, nodeType) => {
event.dataTransfer.setData('application/reactflow', nodeType);
event.dataTransfer.effectAllowed = 'move';
};
return (
<div className='side_controls'>
<div className='add_node_area'>
<h5>Add Nodes</h5>
<div className="description">You can drag these nodes to the pane on the left.</div>
<div className="react-flow__node-input" onDragStart={(event) => onDragStart(event, 'input')} draggable>
Input Node
</div>
<div className="react-flow__node-default" onDragStart={(event) => onDragStart(event, 'default')} draggable>
Default Node
</div>
<div className="react-flow__node-output" onDragStart={(event) => onDragStart(event, 'output')} draggable>
Output Node
</div>
</div>
<div className='save_restore_buttons'>
<button onClick={onSave}>save</button>
<button onClick={onRestore}>restore</button>
</div>
</div>
);
};
export default Sidebar;
You can access these functions inside Sidebar component by passing them as props
<Sidebar onSave={onSave} onRestore={onRestore} />
And in sidebar.js
const Sidebar = (props) => {
const {onSave, onRestore} = props;
Just pass it in as props from hero.js
<Sidebar onSave={onSave} onRestore={onRestore} />
then use it in sidebar.js
const Sidebar = (props) => {
const {onSave, onRestore} = props;
return (
<div className='save_restore_buttons'>
<button onClick={onSave}>save</button>
<button onClick={onRestore}>restore</button>
</div>
)
}
you should pass your functions as props to the Component that you want to use in there, and then in the destination component, you can use those functions with props.[prop-name]
For more information, you can read this doc.
hero.js
<Sidebar onSave={onSave} onRestore={onRestore}/>
Sidebar.js
<div className='save_restore_buttons'>
<button onClick={props.onSave}>save</button>
<button onClick={props.onRestore}>restore</button>
</div>
I am trying to build a React Recipe App. I am making API request for Recipe Search.
My question is about using map in this line
{recipes.map((recipe) => (
<Recipe />
))}
We are trying to access the objects inside of the array. Right? Yes, this works. However, I don't get why
{recipes.forEach ((recipe) => (
<Recipe />
))}
doesn't work. Could you please explain? The whole code is below.
App.js
import React, { useEffect, useState } from "react";
import Recipe from "./Recipe";
import "./App.css";
function App() {
const APP_ID = "xxxxxxxxxxx";
const APP_KEY = "yyyyyyyyyyyyyyyyyyy";
const [recipes, setRecipes] = useState([]);
useEffect(() => {
getRecipes();
}, []);
//yeni bir fonksiyon yazıyorum. take care of fetching all data
const getRecipes = async () => {
const response = await fetch(
`https://api.edamam.com/search?q=chicken&app_id=${APP_ID}&app_key=${APP_KEY}`
);
const data = await response.json();
setRecipes(data.hits);
};
return (
<div className="App">
<form className="search-form">
<input className="search-bar" type="text" />
<button className="search-button" type="submit">
Search
</button>
</form>
{recipes.map((recipe) => (
<Recipe />
))}
</div>
);
}
export default App;
Recipe.js
import React from "react";
const Recipe = () => {
return (
<div>
<h1>Title</h1>
<p>Calories</p>
<img src="" alt="" />
</div>
);
};
export default Recipe;
Because .map() returns the ARRAY of the <Recipe /> entities, and .forEach() doesn't return ANYTHING. React does not render by side-effect - it needs the JSX returned.
The forEach version doesn’t return an array of rendered elements like the map version does. The elements rendered inside the forEach don’t exist outside the scope of the forEach function, whereas the map version returns the elements in an array.
I'm trying to simply pass the Id of a clicked item to display on another page under a different component ("Cart") . At the bottom of the code below, I have a button containing <Cart test={product.id} /> which extracts the Id that I want to be displayed in "Cart" when the button is clicked.
However, I am instead getting an error message of:
Objects are not valid as a React child (found: object with keys
{history, location, match, staticContext}). If you meant to render a
collection of children, use an array instead.
Is there a simple syntax error?
import React, { useState, useEffect, Cart } from 'react';
import './../App.css';
import * as ReactBootStrap from 'react-bootstrap';
function Item(props) {
const [product, setProduct] = useState([]);
const [loading, setLoading] = useState(false);
const [quantity, setQuantity] = useState(1);
const [cost, setCost] = useState([]);
useEffect(async () => {
fetchItems();
}, []);
const itemId = props.match.params.item;
const fetchItems = async () => {
const data = await fetch('https://fakestoreapi.com/products/' + itemId);
const items = await data.json();
setProduct(items);
setLoading(true);
setCost(items.price);
};
function priceUSD(change) {
return change.toFixed(2);
}
useEffect(() => {
const newCost = quantity * product.price;
setCost(priceUSD(newCost));
}, [quantity]);
return (
<div className="App">
<h2>Item</h2>
<div className="gridContainer">
{loading ? (
<div key={itemId} className="productStyle">
<img src={product.image} className="productImage"></img>
<p>{product.title}</p>
<p>{product.description}}</p>
<p>${priceUSD(product.price)}</p>
<div className="quantity">
<button
className="btn minus-btn"
type="button"
onClick={quantity > 1 ? () => setQuantity(quantity - 1) : null}
>
-
</button>
<input type="text" id="quantity" value={quantity} />
<button className="btn plus-btn" type="button" onClick={() => setQuantity(quantity + 1)}>
+
</button>
</div>
<button type="button" onClick={() => <Cart test={product.id} />}>
Add to shopping cart ${cost}
</button>
</div>
) : (
<ReactBootStrap.Spinner className="spinner" animation="border" />
)}
</div>
</div>
);
}
export default Item;
Cart
import React, { useState, Item } from 'react';
import './../App.css';
import './Item.js';
function Cart(test) {
return (
<div className="App">
<p>{test}</p>
</div>
);
}
export default Cart;
Component props are objects. You can read more about them in the official documentation.
You can either destructure the props of the Cart component:
function Cart({test}) {
return (
<div className="App">
<p>{test}</p>
</div>
);
}
or use explicitly test property of props:
function Cart(props) {
return (
<div className="App">
<p>{props.test}</p>
</div>
);
}