Can you help me with React.js history.push function?
I have a icon which can be pressed. The onClick calls handleRemoveFavourite function which filters out the current item from localStrorage and sets the updated string to storage. This works fine.
After the storage update is done the program should reroute the user to the root page /favourites. The reroute works well in the bottom example. But how to do this in the handleRemoveFavourites function?
This is the code I would like to have
handleRemoveFavourite = () => {
const { name } = this.props.workout;
let savedStorage = localStorage.saved.split(",");
let cleanedStorage = savedStorage.filter(function(e) {
return e !== name;
});
localStorage.setItem("saved", cleanedStorage.toString());
history.push("/favourites")
};
renderHeartIcon = () => {
return (
<Route
render={({ history }) => (
<Rating
icon="heart"
defaultRating={1}
maxRating={1}
onClick={this.handleRemoveFavourite}
/>
)}
/>
);
};
The rerouting works fine with just this:
renderHeartIcon = () => {
return (
<Route
render={({ history }) => (
<Rating
key={1}
icon="heart"
defaultRating={1}
maxRating={1}
size="large"
onClick={() => history.push("/favourites")}
/>
)}
/>
);
};
The whole component looks like this:
import React from "react";
import {
Container,
Grid,
Icon,
Label,
Table,
Header,
Rating,
Segment
} from "semantic-ui-react";
import { Link, Route } from "react-router-dom";
export default class WorkoutComponent extends React.PureComponent {
renderChallengeRow = workouts => {
let { reps } = this.props.workout;
reps = reps.split(",");
return workouts.map((item, i) => {
return (
<Table.Row key={item.id}>
<Table.Cell width={9}>{item.name}</Table.Cell>
<Table.Cell width={7}>{reps[i]}</Table.Cell>
</Table.Row>
);
});
};
handleRemoveFavourite = () => {
const { name } = this.props.workout;
let savedStorage = localStorage.saved.split(",");
let cleanedStorage = savedStorage.filter(function(e) {
return e !== name;
});
localStorage.setItem("saved", cleanedStorage.toString());
// history.push("/favourites");
};
renderHeartIcon = () => {
return (
<Route
render={({ history }) => (
<Rating
key={1}
icon="heart"
defaultRating={1}
maxRating={1}
size="large"
onClick={this.handleRemoveFavourite}
/>
)}
/>
);
};
render() {
const { name, workouts } = this.props.workout;
const url = `/savedworkout/${name}`;
return (
<Grid.Column>
<Segment color="teal">
<Link to={url}>
<Header as="h2" to={url} content="The workout" textAlign="center" />
</Link>
<Table color="teal" inverted unstackable compact columns={2}>
<Table.Body>{this.renderChallengeRow(workouts)}</Table.Body>
</Table>
<br />
<Container textAlign="center">
<Label attached="bottom">{this.renderHeartIcon()}</Label>
</Container>
</Segment>
<Link to="/generate">
<Icon name="angle double left" circular inverted size="large" />
</Link>
</Grid.Column>
);
}
}
Since you are using react-router you can use withRouter to achive this.
import { withRouter } from 'react-router-dom'
Wrap the with class name with withRouter.
Like this:
instead of doing like this:
export default class App .....
Separate this like:
class App ...
At the end of the line:
export default withRouter(App)
Now you can use like this:
handleRemoveFavourite = () => {
const { name } = this.props.workout;
let savedStorage = localStorage.saved.split(",");
let cleanedStorage = savedStorage.filter(function(e) {
return e !== name;
});
localStorage.setItem("saved", cleanedStorage.toString());
this.props.history.push("/favourites");
};
You can remove Route from renderHearIcon():
renderHeartIcon = () => {
return (
<Rating
key={1}
icon="heart"
defaultRating={1}
maxRating={1}
size="large"
onClick={this.handleRemoveFavourite}
/>
);
};
onClick={this.handleRemoveFavourite}
=>
onClick={()=>this.handleRemoveFavourite(history)}
handleRemoveFavourite = () => {
=>
handleRemoveFavourite = (history) => {
The problem you are facing is, you are providing <Route> with the history prop, so that it is propagated on the component function call. But you are not propagating it to the handleRemoveFavourite function.
You'll need to wrap this. handleRemoveFavourite in an anonymous function call. Like
onClick={() => this.handleRemoveFavourite(history)}
and then accept it as a valid argument in your function
handleRemoveFavourite = (history) => {...}
that should solve it
Changing to this.props.history.push("/favourites"); should works.
EDITED
Is your component inside a Route? If so, this.props.history will works. I tested changing your class to run on my project.
import React from 'react';
import ReactDOM from 'react-dom';
import {
Container,
Grid,
Icon,
Label,
Table,
Header,
Rating,
Segment
} from "semantic-ui-react";
import { Link, Route, BrowserRouter as Router } from "react-router-dom";
export default class WorkoutComponent extends React.Component {
static defaultProps = {
workout: {
reps: "1,2,3,4",
workouts: []
},
}
renderChallengeRow = workouts => {
let { reps } = this.props.workout;
reps = reps.split(",");
return workouts.map((item, i) => {
return (
<Table.Row key={item.id}>
<Table.Cell width={9}>{item.name}</Table.Cell>
<Table.Cell width={7}>{reps[i]}</Table.Cell>
</Table.Row>
);
});
};
handleRemoveFavourite = () => {
const { name } = this.props.workout;
//let savedStorage = localStorage.saved.split(",");
// let cleanedStorage = savedStorage.filter(function(e) {
// return e !== name;
// });
// localStorage.setItem("saved", cleanedStorage.toString());
this.props.history.push("/favourites");
};
renderHeartIcon = () => {
return (
<Route
render={({ history }) => (
<Rating
key={1}
icon="heart"
defaultRating={1}
maxRating={1}
size="large"
onClick={this.handleRemoveFavourite}
/>
)}
/>
);
};
render() {
console.log(this.props)
const { name, workouts } = this.props.workout;
const url = `/savedworkout/${name}`;
return (
<Grid.Column>
<Segment color="teal">
<Link to={url}>
<Header as="h2" to={url} content="The workout" textAlign="center" />
</Link>
<Table color="teal" inverted unstackable compact columns={2}>
<Table.Body>{this.renderChallengeRow(workouts)}</Table.Body>
</Table>
<br />
<Container textAlign="center">
<Label attached="bottom">{this.renderHeartIcon()}</Label>
</Container>
</Segment>
<Link to="/generate">
<Icon name="angle double left" circular inverted size="large" />
</Link>
</Grid.Column>
);
}
}
ReactDOM.render(
<Router>
<Route path="/" component={ WorkoutComponent }/>
</Router>, document.getElementById('root'));
Related
I am trying to add the items to a cart page when a user clicks the add to cart button.
import React from "react";
import "bootstrap";
import { useParams } from "react-router-dom";
function ItemDetail(handleClick) {
const params = useParams();
let { productCode, vendor, value} = params;
let item = {productCode, vendor, value};
console.log(item);
return (
<>
<div>
<p>product id: {productCode}</p>
<p>price: {value}</p>
<p>vendor: {vendor}</p>
<button onClick={() => handleClick(item)}>Add to Cart</button>
</div>
</>
);
}
export default ItemDetail;
This is the cart page. Where I am to, render the item details from Item Details Page.
import React, { useState, useEffect } from "react";
const Cart = ({ cart, setCart, handleChange }) => {
const [price, setPrice] = useState(0);
const handleRemove = (id) => {
const arr = cart.filter((item) => item.id !== id);
setCart(arr);
handlePrice();
};
const handlePrice = () => {
let ans = 0;
cart.map((item) => (ans += item.amount * item.price));
setPrice(ans);
};
useEffect(() => {
handlePrice();
});
console.log(setCart);
return (
<article>
{cart.map((item) => (
<div className="cart_box" key={item.id}>
<div>
<button onClick={() => handleChange(item, 1)}>+</button>
<button>{item.amount}</button>
<button onClick={() => handleChange(item, -1)}>-</button>
</div>
<div>
<span>{item.price}</span>
<button onClick={() => handleRemove(item.id)}>Remove</button>
</div>
</div>
))}
<div className="total">
<span>Total Price of your Cart</span>
<span>R - {price}</span>
</div>
</article>
);
};
export default Cart;
This is my item description page. I have fetched the items using params, this is only way I found easier for me.
import React, { useState, useEffect } from "react";
import { Row, Col } from "react-bootstrap";
import StyledCard from "../components/Card";
const Discover = (props, params, handleClick) => {
const token = "not-the-actual-token";
const [result, setResult] = useState([]);
useEffect(() => {
fetch(
"https://api.flash-internal.flash-group.com/ecommerceManagement/1.0.0/api/product/",
{
method: "GET",
headers: { Authorization: `Bearer ${token}` },
}
)
.then((res) => res.json())
.then((json) => setResult(json));
}, []);
const cardStyle = {
listStyle: "none",
margin: 5,
paddingLeft: 0,
minWidth: 240,
};
return (
<>
<div className="latestdeals container my-5">
<h1>All Products</h1>
<Row className="hotcards">
<Col className="colcard">
{(result?.result || []).map((item) => (
<div key={item.productCode} style={cardStyle}>
<a href={`/itemDetail/${item.productCode}/${item.value}/${item.vendor}`}>
{" "}
<StyledCard
key={item.productCode}
name={item.vendor}
title={item.description}
price={item.value}
handleClick={handleClick}
item={item}
/>
</a>
</div>
))}
</Col>
</Row>
</div>
</>
);
};
export default Discover;
This is my App page
import "./index.scss";
import React, { useState } from "react";
import {
BrowserRouter as Router,
Route,
Routes,
useParams,
} from "react-router-dom";
import AllCategories from "./pages/all-catergories";
import Home from "./pages/home";
import Entertainment from "./pages/entertainment";
// import Cart from "./pages/_cart";
import Login from "./pages/login";
import Netflix from "./pages/netflix";
import Orders from "./pages/orders";
import SignUp from "./pages/sign-up";
// import Data2 from "./Data2";
import Products from "./pages/products";
// import Shop from "./components/Shop";
// import ProductDetail from "./pages/ProductDetail";
import Discover from "./pages/discover";
import ItemDetail from "./pages/itemDetail";
import Cart from "./pages/cart";
function App() {
const [show, setShow] = useState(true);
const [cart, setCart] = useState([]);
const handleClick = (item) => {
if (cart.indexOf(item) !== -1) return;
setCart([...cart, item]);
};
const handleChange = (item, d) => {
const ind = cart.indexOf(item);
const arr = cart;
arr[ind].amount += d;
if (arr[ind].amount === 0) arr[ind].amount = 1;
setCart([...arr]);
};
return (
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="all-categories" exact element={<AllCategories />} />
{/* <Route path="cart" exact element={<Cart />} /> */}
<Route path="entertainment" exact element={<Entertainment />} />
<Route path="login" exact element={<Login />} />
<Route path="discover" exact element={<Discover />} />
<Route path="netflix" exact element={<Netflix />} />
<Route path="orders" exact element={<Orders />} />
<Route path="sign-up" exact element={<SignUp />} />
<Route path="products" element={<Products />} />
<Route path="/itemDetail/:productCode/:value/:vendor" element={<ItemDetail />} />
<Route path="/itemDetail/" element={<ItemDetail handleClick={handleClick} />} />
<Route path="/Cart/" exact element={<Cart cart={cart} setCart={setCart} handleChange={handleChange}/>} />
</Routes>
</Router>
);
}
export default App;
Issues
You've issues declaring React components, several of them aren't using props correctly. function ItemDetail(handleClick) { ... } should be function ItemDetail({ handleClick }) { ... }, and const Discover = (props, params, handleClick) => { ... } should probably be something like const Discover = ({ params, handleClick, ...props }) => { ... }. React components receive a single props object argument.
handleChange in App is also mutating state.
Solution
App
Fix the state mutation and ensure props are passed correctly to routed components. Use an item GUID to search the cart instead of shallow reference equality when checking to add to the cart. When updating cart quantities it is necessary to shallow copy the cart array and cart items that are being updated. Use functional state updates whenever possible so it's ensured it's updating from the previous state and not any stale state value closed over in scope.
function App() {
const [show, setShow] = useState(true);
const [cart, setCart] = useState([]);
const handleClick = (item) => {
// Update cart item quantity if already in cart
if (cart.some((cartItem) => cartItem.productCode === item.productCode)) {
setCart((cart) =>
cart.map((cartItem) =>
cartItem.productCode === item.productCode
? {
...cartItem,
amount: cartItem.amount + 1
}
: cartItem
)
);
return;
}
// Add to cart
setCart((cart) => [
...cart,
{ ...item, amount: 1 } // <-- initial amount 1
]);
};
const handleChange = (productCode, d) => {
setCart((cart) =>
cart.flatMap((cartItem) =>
cartItem.productCode === productCode
? cartItem.amount + d < 1
? [] // <-- remove item if amount will be less than 1
: [
{
...cartItem,
amount: cartItem.amount + d
}
]
: [cartItem]
)
);
};
return (
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="all-categories" element={<AllCategories />} />
<Route path="entertainment" element={<Entertainment />} />
<Route path="login" element={<Login />} />
<Route path="discover" element={<Discover />} />
<Route path="netflix" element={<Netflix />} />
<Route path="orders" element={<Orders />} />
<Route path="sign-up" element={<SignUp />} />
<Route path="products" element={<Products />} />
<Route
path="/itemDetail/:productCode/:value/:vendor"
element={<ItemDetail handleClick={handleClick} />}
/>
<Route
path="/Cart/"
element={(
<Cart
cart={cart}
setCart={setCart}
handleChange={handleChange}
/>
)}
/>
</Routes>
</Router>
);
}
ItemDetail
Access/destructure the handleClick prop correctly. Pass the item's productCode to the callback.
function ItemDetail({ handleClick }) {
const { productCode, vendor, value} = useParams();
const item = { productCode, vendor, value };
return (
<div>
<p>product id: {productCode}</p>
<p>price: {value}</p>
<p>vendor: {vendor}</p>
<button onClick={() => handleClick(item)}>Add to Cart</button>
</div>
);
}
Discover
Correctly access/destructure the handleClick callback. Use the Link component instead of the raw anchor (<a />) tag. The anchor tag will reload the app which very likely isn't what you want to happen. Based on the code I suspect you don't actually need this handleClick since the ItemDetail component is passed it and adds to the cart
import { Link } from 'react-router-dom';
const cardStyle = {
listStyle: "none",
margin: 5,
paddingLeft: 0,
minWidth: 240,
};
const Discover = () => {
const token = "not-the-actual-token";
const [result, setResult] = useState([]);
useEffect(() => {
fetch(
"https://api.flash-internal.flash-group.com/ecommerceManagement/1.0.0/api/product/",
{
method: "GET",
headers: { Authorization: `Bearer ${token}` },
}
)
.then((res) => {
if (!res.ok) {
throw new Error('Network response was not OK');
}
return res.json();
})
.then((data) => setResult(data.result))
.catch(error => {
// handle any rejected Promises, errors, etc...
});
}, []);
return (
<div className="latestdeals container my-5">
<h1>All Products</h1>
<Row className="hotcards">
<Col className="colcard">
{result.map((item) => (
<div key={item.productCode} style={cardStyle}>
<Link to={`/itemDetail/${item.productCode}/${item.value}/${item.vendor}`}>
<StyledCard
name={item.vendor}
title={item.description}
price={item.value}
item={item}
/>
</Link>
</div>
))}
</Col>
</Row>
</div>
);
};
Cart
Don't store the cart total in state, it is easily derived from the cart state.
const Cart = ({ cart, setCart, handleChange }) => {
const handleRemove = (productCode) => {
setCart(cart => cart.filter(item => item.productCode !== productCode));
};
const price = cart.reduce((total, item) => total + item.amount * item.price, 0);
return (
<article>
{cart.map((item) => (
<div className="cart_box" key={item.id}>
<div>
<button onClick={() => handleChange(item.productCode, 1)}>+</button>
<button>{item.amount}</button>
<button onClick={() => handleChange(item.productCode, -1)}>-</button>
</div>
<div>
<span>{item.price}</span>
<button onClick={() => handleRemove(item.productCode)}>Remove</button>
</div>
</div>
))}
<div className="total">
<span>Total Price of your Cart</span>
<span>R - {price}</span>
</div>
</article>
);
};
I have this serch.js file. When I search and click on the li in the search result, I want to get redirected to <InnerDetail /> but the URL doesn't change or I don't get redirected to this page
but manualy if I type in the URL localhost/detiled/8 I am redirected to to <InnerDetail /> with id as 8
import React from "react";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faSearch } from "#fortawesome/free-solid-svg-icons";
import { useState, useEffect } from "react";
import axios from "axios";
import { Link } from "react-router-dom";
import { useHistory } from "react-router-dom";
const initialState = {
idaddProducts: "",
};
const Searchclients = () => {
const history = useHistory();
const [showResults, setShowResults] = React.useState(true);
const [poName, pnName] = React.useState(initialState);
const [showSerch, setShowSerch] = React.useState([]);
const [detail, setDetail] = useState(false);
const [inputValue, setInputValue] = React.useState("");
const [filteredSuggestions, setFilteredSuggestions] = React.useState([]);
const [selectedSuggestion, setSelectedSuggestion] = React.useState(0);
const [displaySuggestions, setDisplaySuggestions] = React.useState(false);
const suggestions = [];
showSerch.forEach(function (data) {
suggestions.push(data);
});
const onChange = (event) => {
const value = event.target.value;
setInputValue(value);
setShowResults(false);
const filteredSuggestions = suggestions.filter(
(suggestion) =>
suggestion.firstname
.toString()
.toLowerCase()
.includes(value.toLowerCase()) ||
suggestion.id.toString().toLowerCase().includes(value.toLowerCase())
);
setFilteredSuggestions(filteredSuggestions);
setDisplaySuggestions(true);
};
const onSelectSuggestion = (index) => {
setSelectedSuggestion(index);
setInputValue(filteredSuggestions[index]);
setFilteredSuggestions([]);
setDisplaySuggestions(false);
};
const SuggestionsList = (props) => {
// console.log(props);
const {
suggestions,
inputValue,
onSelectSuggestion,
displaySuggestions,
selectedSuggestion,
} = props;
if (inputValue && displaySuggestions) {
if (suggestions.length > 0) {
return (
<ul className="suggestions-list" style={styles.ulstyle}>
{suggestions.map((suggestion, index) => {
// console.log(suggestions);
const isSelected = selectedSuggestion === index;
const classname = `suggestion ${isSelected ? "selected" : ""}`;
return (
<Link to={`/detiled/${suggestion.id}`}> //this link dont work
<li
style={styles.listyle}
// onMouseOver={{ background: "yellow" }}
key={index}
className={classname}
>
{suggestion.firstname}
</li>
</Link>
);
})}
</ul>
);
} else {
return <div>No suggestions available...</div>;
}
}
return <></>;
};
useEffect(() => {
axios
.get("all-doctors-list/")
.then((res) => {
const data = res.data;
// pnName(data.data);
// var stringdata = data;
setShowSerch(data);
//console.log(stringdata);
});
// setShowSerch(data);
}, []);
return (
<>
<div className="note-container" style={styles.card}>
<div style={styles.inner}>
<p style={{ textAlign: "left" }}>Search Doctors</p>
<form className="search-form" style={{}}>
{showResults ? (
<FontAwesomeIcon
style={{ marginRight: "-23px" }}
icon={faSearch}
/>
) : null}
<input
onChange={onChange}
value={inputValue}
style={styles.input}
type="Search"
/>
<SuggestionsList
inputValue={inputValue}
selectedSuggestion={selectedSuggestion}
onSelectSuggestion={onSelectSuggestion}
displaySuggestions={displaySuggestions}
suggestions={filteredSuggestions}
/>
</form>
</div>
</div>
</>
);
};
export default Searchclients;
nav.js
import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import InnerDetail from "./client/doctor/components/innerto_detail.js";
class Navigator extends React.Component {
render() {
return (
<Router>
<div>
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/detiled/:id">
<InnerDetail />
</Route>
</div>
</Router>
);
}
}
function Home() {
return (
<div style={{ paddingTop: "20%", textAlign: "center" }}>
<h1>Home</h1>
</div>
);
}
export default Navigator;
You are missing closing tag of Switch:
import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import InnerDetail from "./client/doctor/components/innerto_detail.js";
class Navigator extends React.Component {
render() {
return (
<Router>
<div>
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/detiled/:id">
<InnerDetail />
</Route>
</Switch>
</div>
</Router>
);
}
}
function Home() {
return (
<div style={{ paddingTop: "20%", textAlign: "center" }}>
<h1>Home</h1>
</div>
);
}
export default Navigator;
Also its a tipo but you may want to say detailed, instead of detiled, but that does not affect.
I want to add to a state array, but I find that any code I write that brings up the state leads to the same error one the browser tries to run it.
Even just a console.log triggers it.
var folderList = this.state.folders;
console.log("folderList: " + folderList);
The full code:
import React, {Component} from 'react';
import {Route, Link} from 'react-router-dom';
import {FontAwesomeIcon} from '#fortawesome/react-fontawesome';
import NoteListNav from '../NoteListNav/NoteListNav';
import NotePageNav from '../NotePageNav/NotePageNav';
import NoteListMain from '../NoteListMain/NoteListMain';
import NotePageMain from '../NotePageMain/NotePageMain';
import dummyStore from '../dummy-store';
import {getNotesForFolder, findNote, findFolder} from '../notes-helpers';
import './App.css';
import AddFolder from '../AddFolder/AddFolder';
import AddNote from '../AddNote/AddNote';
class App extends Component {
state = {
notes: [],
folders: [],
//noteID: 0,
//folderID: 0
};
componentDidMount() {
// fake date loading from API call
setTimeout(() => this.setState(dummyStore), 600);
}
folderSubmit(f){
console.log("folderSubmit ran " + f);
var folderList = this.state.folders;
console.log("folderList: " + folderList);
//this.setState({ folders: joined })
}
renderNavRoutes() {
const {notes, folders} = this.state;
return (
<>
{['/', '/folder/:folderId'].map(path => (
<Route
exact
key={path}
path={path}
render={routeProps => (
<NoteListNav
folders={folders}
notes={notes}
{...routeProps}
/>
)}
/>
))}
<Route
path="/note/:noteId"
render={routeProps => {
const {noteId} = routeProps.match.params;
const note = findNote(notes, noteId) || {};
const folder = findFolder(folders, note.folderId);
return <NotePageNav {...routeProps} folder={folder} />;
}}
/>
<Route path="/add-folder" component={NotePageNav} />
<Route path="/add-note" component={NotePageNav} />
</>
);
}
renderMainRoutes() {
const {notes, folders} = this.state;
return (
<>
{['/', '/folder/:folderId'].map(path => (
<Route
exact
key={path}
path={path}
render={routeProps => {
const {folderId} = routeProps.match.params;
const notesForFolder = getNotesForFolder(
notes,
folderId
);
return (
<NoteListMain
{...routeProps}
notes={notesForFolder}
/>
);
}}
/>
))}
<Route
path="/note/:noteId"
render={routeProps => {
const {noteId} = routeProps.match.params;
const note = findNote(notes, noteId);
return <NotePageMain {...routeProps} note={note} />;
}}
/>
<Route
path="/add-folder"
render={routeProps => {
return <AddFolder addNewFolder={this.folderSubmit}/>
}}
/>
<Route
path="/add-note"
render={routeProps => {
return <AddNote/>
}}
/>
</>
);
}
render() {
return (
<div className="App">
<nav className="App__nav">{this.renderNavRoutes()}</nav>
<header className="App__header">
<h1>
<Link to="/">Noteful</Link>{' '}
<FontAwesomeIcon icon="check-double" />
</h1>
</header>
<main className="App__main">{this.renderMainRoutes()}</main>
</div>
);
}
}
export default App;
this will not be what you expect inside of folderSubmit. How to get around this is discussed in the "Handling Events" part of the documentation. You could bind it to this in the constructor, or use an arrow function as a class field.
folderSubmit = (f) => {
var folderList = this.state.folders;
console.log("folderList:", folderList);
}
This is react-window plugin: https://github.com/bvaughn/react-window
I am using this to render simple list of "Rows".
This is Row comp in which I am try to pass function and const idTestProps=''
class Row extends PureComponent {
render() {
const { index, style } = this.props;
let label;
if (itemStatusMap[index] === LOADED) {
label = `Row ${index}`;
} else {
label = "Loading...";
}
return (
<div className="ListItem" style={style}>
{label}
</div>
);
}
}
This is the Container comp which should pass function and one props to the Row comp:
const outerElementType = forwardRef((props, ref) => (
<div ref={ref} onClick={handleClick} {...props} />
));
export default function App() {
return (
<Fragment>
<InfiniteLoader
isItemLoaded={isItemLoaded}
itemCount={1000}
loadMoreItems={loadMoreItems}
>
{({ onItemsRendered, ref }) => (
<List
className="List"
height={150}
itemCount={1000}
itemSize={35}
// This is outerElementType is way to pass some function down to Row
outerElementType={outerElementType}
width={300}
>
{Row}
</List>
)}
</Fragment>
);
I successfully pass 'function' and works but property not.
How to pass props down in same time with function?
This is codesandbox example:
https://codesandbox.io/s/4zqx79nww0
I have never used react-window but maybe you can do something like this:
import React, { forwardRef } from "react";
import ReactDOM from "react-dom";
import { FixedSizeList as List } from "react-window";
import "./styles.css";
const Row = props => ({ index, style }) => (
<div className={index % 2 ? "ListItemOdd" : "ListItemEven"} style={style}>
Row {index} {props.test}
</div>
);
function handleOnWheel({ deltaY }) {
// Your handler goes here ...
console.log("handleOnWheel()", deltaY);
}
const outerElementType = forwardRef((props, ref) => (
<div ref={ref} onWheel={handleOnWheel} {...props} />
));
const Example = () => (
<List
className="List"
height={150}
itemCount={1000}
itemSize={35}
outerElementType={outerElementType}
width={300}
>
{Row({ test: "test" })}
</List>
);
ReactDOM.render(<Example />, document.getElementById("root"));
I'm building a React news app that gets its data from News API. On the home page I have a search bar where user enters key words to retreive from the API. When I enter the key word and press enter, the state changes and the results are visible on the page but then immediately it refreshes and displays the default page.
App.js:
class App extends Component {
constructor(props) {
super(props);
this.state = { articles: [], keyword: ''};
this.fetchNewsWithKeywords = this.fetchNewsWithKeywords.bind(this);
}
fetchNewsWithKeywords(keyword){
searchForKeywords(keyword)
.then(articles => this.setState({ articles: articles, keyword: keyword}))
}
render() {
return (
<Router >
<div className="App">
<div className="container" >
<Header/>
<Route exact path="/" render={props => (
<React.Fragment>
<SearchNews fetchNewsWithKeywords = {this.fetchNewsWithKeywords.bind(this)}/>
<NewsList articles = {this.state.articles}/>
</React.Fragment>
)} />
<Route path="/top-headlines" component={TopHeadlines} />
<Route path="/newest" component={Newest} />
</div>
</div>
</Router>
);
}
}
export default App;
SearchNews.js
class SearchNews extends Component {
state = {
value: ""
}
onSubmit = (e) => {
var str = this.state.value;
this.props.fetchNewsWithKeywords(str)
}
handleOnChange = event => {
this.setState({
value: event.target.value
})
};
render() {
const { classes } = this.props;
return (
<form className={classes.container} noValidate autoComplete="off" onSubmit={this.onSubmit}>
<TextField
id="outlined-search"
label="Search"
type="search"
className={classes.textField}
margin="normal"
variant="outlined"
onChange={this.handleOnChange}
/>
</form>
)
}
}
function for retrieving the data from API
export async function searchForKeywords(keyword){
var query = keyword
var url = "https://newsapi.org/v2/everything?q="+
encodeURIComponent(query) +
"&apiKey="+API_KEY;
let result = await fetch(url).then(response => response.json());
return result.articles.slice(0,20);
NewsList.js
export class NewsList extends Component {
render() {
return this.props.articles.map((article) => (
<div className="gridContainer">
<div className="gridItem" >
<Article article = {article}/>
</div>
</div>
));
}
}
export default NewsList
Article.js
class Article extends Component {
render() {
const {
title,
description,
publishedAt,
source,
urlToImage,
url
} = this.props.article;
const { classes } = this.props;
let date = new Date(publishedAt).toLocaleString();
return (
<Card className={classes.card} >
<CardActionArea href={url} target="_blank">
<CardMedia
className={classes.media}
image={urlToImage}
title={title}
/>
<CardContent >
<Typography gutterBottom variant="h5" component="h2">
{title}
</Typography>
<Typography component="p">
{description}
</Typography>
<Typography variant="caption">
{source.name}
</Typography>
<Typography variant="caption">
{date}
</Typography>
</CardContent>
</CardActionArea>
</Card>
);
}
}
export default withStyles(styles)(Article);
I think your problem is here:
let result = await fetch(url).then(response => response.json());
return result.articles.slice(0,20);
You are awaiting the fetch and then .then to get the response as json, however, you are returning the results.articles.slice prior to that response.json() resolving?
Try:
let result = await fetch(url)
result = await result.json()
return result.articles.slice(0,20);