This question already has answers here:
Unable to pass props to component through react-router
(3 answers)
When I link javascript file in html it sends a request to server and causing error
(3 answers)
Closed 4 years ago.
I am having a trouble with this minor problem . When i refresh my page , it gives an error `
GET http://localhost:3000/drink/bundle.js net::ERR_ABORTED 404 (Not Found)
Refused to apply style from 'http://localhost:3000/drink/style.css' because its MIME type ('text/html') is not a supported stylesheet MIME type, and strict MIME checking is enabled.
I know that this a problem with rout but i couldnt find a way to solve it .
Here is my code `
import * as React from "react"; import {connect} from "react-redux"; import {drinksSelector} from "../selectors" import {getDrinksList, getNameFilter} from "../actions"; import Drink from "./Drink"; import SearchIcon from "./SvgIcons";
class Drinks extends React.Component {
componentDidMount() {
this.props.dispatchDrinksList();
}
handleInputChange = (e: any) => {
e.preventDefault();
this.props.dispatchNameFilter(e.target.value) }
render() {
const {drinks} = this.props;
return (
<div>
<header className="main-header">
<form className="main-header__from">
<div className="search-wrapper">
<input autoComplete='off'
type="text"
name="search"
placeholder="Search"
className="main-header__input"
onChange={this.handleInputChange}/>
<SearchIcon />
</div>
</form>
</header>
<ul>
{drinks.map((drink: any) => <Drink key={drink.idDrink} url={drink.strDrinkThumb} name={drink.strDrink} id={drink.idDrink}/>)}
</ul>
</div>
); } } const mapStateToProps: any = (state: any) => drinksSelector(state);
const mapDispatchToProps: any = (dispatch: ReturnType<typeof mapDispatchToProps>) => ({ dispatchDrinksList() {
dispatch(getDrinksList()); },
dispatchNameFilter(value: any) {
dispatch(getNameFilter(value));
} });
export default connect(mapStateToProps, mapDispatchToProps)(Drinks);
Above is the dashboard page where i render list of drinks, and when clicking on them the routes changes to /drink/someId
import * as React from 'react'; import { connect } from 'react-redux';
import { NavLink } from "react-router-dom";
const Drink = ({ url = "", name, id }: any): any => (
<li>
<NavLink to={`/drink/${id}`}>
<h2>{name}</h2>
<img src={url} alt={name} height="350" width="350"/>
</NavLink>
</li> );
export default Drink;
And this is the page where after refreshing i got an error `
import * as React from 'react';
import { connect } from 'react-redux';
import { NavLink } from "react-router-dom";
import {getDrinkInfo} from "../actions";
class DrinkInfo extends React.Component<any, any> {
componentDidMount() {
const id = localStorage.getItem("id");
if (!id) {
localStorage.setItem("id", this.props.match.params.id);
}
this.props.dispatchDrinkInfo(localStorage.getItem("id"));
}
render() {
const { drink } = this.props;
console.log(this.props, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
return (
<div className="drink-info">
<header className="drink-info__header">
<button onClick={() => this.props.history.goBack()} className="drink-info__button">Go Back</button>
<h2 className="drink-info__title">{drink && drink.strDrink}</h2>
</header>
<section className="details">
<div className="details__image-box">
<img src={drink && drink.strDrinkThumb} alt={drink && drink.strDrink} height="350" width="350"/>
</div>
<div className="details__info">
<p className="details__ingredients"></p>
<div className="details__prepare">
<h3 className="details__prepare-heading">How to prepare</h3>
<p className="details__prepare-text">
{drink && drink.strInstructions}
</p>
</div>
</div>
</section>
</div>
)
}
}
const mapStateToProps: any = (state: any) => {
return {
drink: state.drink.get("drinkInfo") || {}
}
}
const mapDispatchToProps: any = (dispatch: ReturnType<typeof mapDispatchToProps>) => ({
dispatchDrinkInfo(id: number) {
dispatch(getDrinkInfo(id));
}
});
export default connect(mapStateToProps, mapDispatchToProps)(DrinkInfo);
My routers `
import * as React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import DashboardPage from '../components/DashboardPage';
import DrinkInfo from '../components/DrinkInfo';
import NotFoundPage from '../components/NotFoundPage';
const AppRouter = () => (
<BrowserRouter>
<React.Fragment>
<Switch>
<Route path="/" component={DashboardPage} exact/>
<Route path="/drink/:id" component={DrinkInfo}/>
<Route component={NotFoundPage} />
</Switch>
</React.Fragment>
</BrowserRouter>
);
export default AppRouter;
Related
I started to learn React, I'm trying to retrieve data from api, the data is an object with the fields of base, date & rates, without any problem I can print and logout base & date but rates which is an object not.
console.log gives undefined, when trying to iterate is obviously that the object does not exist but in DevTools i can see normal data
Thank you for your help and greetings
Context:
export const ExchangeProvider = props => {
const [lastestExchanges, setLastestExchanges] = useState({})
const fetchLastestExchange = async () => {
try {
await fetch(`https://api.exchangeratesapi.io/latest`).then(data => data.json()).then(data => setLastestExchanges(data))
} catch (err) {
console.log(err)
}
}
useEffect(() => {
fetchLastestExchange()
}, [])
return (
<ExchangeContext.Provider value={[lastestExchanges, setLastestExchanges]}>
{props.children}
</ExchangeContext.Provider>
)
}
Usage:
import React, {useState, useContext} from "react";
import {ExchangeContext} from "../ExchangeContext";
function HomeView() {
const [lastestExchange, setLastestExchange] = useContext(ExchangeContext)
console.log(lastestExchange)
return (
<div className="container">
<p>{lastestExchange.base}</p>
<p>{lastestExchange.date}</p>
{/*<p>{lastestExchange.rates['PLN']}</p>*/}
<ul>
{/*{Object.keys(lastestExchange.rates).map(key => <li>{lastestExchange.rates[key]}</li>)}*/}
</ul>
</div>
)
}
export default HomeView
Provider usage:
import React from 'react';
import HomeView from "./Views/HomeView";
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
import {ExchangeProvider} from "./ExchangeContext";
function App() {
return (
<ExchangeProvider>
<div className="App container w-full flex h-full">
<Router>
<Switch>
<Route path="/">
<HomeView/>
</Route>
</Switch>
</Router>
</div>
</ExchangeProvider>
);
}
export default App;
You can use react context simpler like this :
// src/ThemeContext.js
import React from 'react';
const ThemeContext = React.createContext(null);
export default ThemeContext;
// src/ComponentA.js
import React from 'react';
import ThemeContext from './ThemeContext';
const A = () => (
<ThemeContext.Provider value="green">
<D />
</ThemeContext.Provider>
);
// src/ComponentD.js
import React from 'react';
import ThemeContext from './ThemeContext';
const D = () => (
<ThemeContext.Consumer>
{value => (
<p style={{ color: value }}>
Hello World
</p>
)}
</ThemeContext.Consumer>
);
I want to implement the navigation on menu bar which I am fetching from api. For e.g. on home page I have four menus like menu1 menu2 menu3 menu4 which displays always. On click on these menus i want to fetch products related to them.
I have read about nested routes in React js but unable to implement that.
Dynamic menu bar of categories:
import React from 'react';
import './style.css';
import {Link} from 'react-router-dom';
import Api from '../../Api';
class TopMenu extends React.Component {
state = {
categories : []
}
componentDidMount(){
Api.get(`categories`).then(
response => {
this.setState({categories: response.data});
});
};
render(){
return (
<div className="menu">
{this.state.categories.map(category => (
<Link to={"/category/" + category.name} key={category.id} className="menu-item"><span>{category.name}</span></Link>
))}
</div>
);
}
};
export default TopMenu;
My Routes file:
import React from 'react';
import {Switch, Route} from 'react-router-dom';
import CategoryProducts from './CategoryProducts';
import Home from './Home';
const Routes = () => {
return(
<Switch>
<Route path='/' exact component={Home} />
<Route path='/category/:name' component={CategoryProducts} />
</Switch>
);
};
export default Routes;
The click on Category will just change the browser url, not the page.
CategoryProducts.ja
import React from 'react';
import Products from './Products';
class CategoryProducts extends React.Component {
render(){
return (
<div className="content-wrapper">
<div className="menu-left">
<Products/>
</div>
</div>
);
}
}
export default CategoryProducts;
Products.js
import React,{useState, useEffect} from 'react';
import Api from './Api'
import Card from './components/Card';
class Products extends React.Component {
state = {
categories : []
}
componentDidMount(){
let categoryName = this.props.match ? this.props.match.params.name : 'Veg Pizza';
Api.get(`category/${categoryName}`).then(
response => {
this.setState({products: response.data});
});
};
render(){
return (
<div>
<div className="ref">
<div className="menu-hr"></div>
<div className="menu-cat">
<div className="menu-catname ">BESTSELLERS</div>
</div>
</div>
<div className="card-container">
<div className="all-cards" data-label="Bestsellers">
<Card />
</div>
</div>
</div>
);
}
};
export default Products;
Your Products component is not mounting again and again, because this renders on all possible categories. Therefore In order to fetch data for different categories, you might have to use componentDidUpdate lifecycle method.
import React,{useState, useEffect} from 'react';
import Api from './Api'
import Card from './components/Card';
class Products extends React.Component {
state = {
categories : []
}
componentDidMount(){
let categoryName = this.props.match ? this.props.match.params.name : 'Veg Pizza';
Api.get(`category/${categoryName}`).then(
response => {
this.setState({products: response.data});
});
};
componentDidUpdate(prevProps, prevState){
if(prevProps.match.params.name !== this.props.match.params.name){
Api.get(`category/${categoryName}`).then(
response => {
this.setState({products: response.data});
});
}
}
render(){
return (
<div>
<div className="ref">
<div className="menu-hr"></div>
<div className="menu-cat">
<div className="menu-catname ">BESTSELLERS</div>
</div>
</div>
<div className="card-container">
<div className="all-cards" data-label="Bestsellers">
<Card />
</div>
</div>
</div>
);
}
};
export default Products;
If you want to force rerenders anyway and componentDidUpdate doesnt works for you, you can cause force rerender using key prop
import React from 'react';
import Products from './Products';
class CategoryProducts extends React.Component {
render(){
return (
<div className="content-wrapper">
<div className="menu-left">
<Products key={this.props.match.params.name}/>
</div>
</div>
);
}
}
export default CategoryProducts;
Please let me know if ut didnt solve your problem.
Im trying to build a simple app with react-boilerplate, the containers(basically views) and components do not use classes to create new components, instead they are basic functions. Typically you add a constructor or direct state to a class, since there are no classes i dont know where to add the state definition. Nowhere ive added a constructer or state definition works, where can you add simple react (NO redux) state list or constructor within this pattern for this view?
example of a container
/*
* HomePage
*
* This is the first thing users see of our App, at the '/' route
*/
import React, { useEffect, memo } from 'react';
import PropTypes from 'prop-types';
import { Helmet } from 'react-helmet';
import { FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import { compose } from 'redux';
import { createStructuredSelector } from 'reselect';
import { useInjectReducer } from 'utils/injectReducer';
import { useInjectSaga } from 'utils/injectSaga';
import {
makeSelectRepos,
makeSelectLoading,
makeSelectError,
} from 'containers/App/selectors';
import H2 from 'components/H2';
import ReposList from 'components/ReposList';
import AtPrefix from './AtPrefix';
import CenteredSection from './CenteredSection';
import Form from './Form';
import Input from './Input';
import Section from './Section';
import messages from './messages';
import { loadRepos } from '../App/actions';
import { changeUsername, setHomeVisible } from './actions';
import { makeSelectUsername, makeHomeVisible } from './selectors';
import reducer from './reducer';
import saga from './saga';
import NavBar from './NavBar';
import ButtonLink from './ButtonLink';
const key = 'home';
export function HomePage({
username,
loading,
error,
repos,
onSubmitForm,
onChangeUsername,
homeVisible,
makeHomeVisible,
setHomeVisible
}) {
useInjectReducer({ key, reducer });
useInjectSaga({ key, saga });
useEffect(() => {
// When initial state username is not null, submit the form to load repos
if (username && username.trim().length > 0) onSubmitForm();
}, []);
const reposListProps = {
loading,
error,
repos,
};
return (
<article>
<Helmet>
<title>title</title>
<meta
name="description"
content=""
/>
</Helmet>
<NavBar>
<ButtonLink to="/" onClick={makeHomeVisible}>
<FormattedMessage {...messages.websites} />
</ButtonLink>
<ButtonLink to="/apps" >
<FormattedMessage {...messages.apps} />
</ButtonLink>
</NavBar>
<div className={`home ${this.state.visible === false ? 'hidden': ''}`}>
<Section>
<H2>
<FormattedMessage {...messages.trymeHeader} />
</H2>
<Form onSubmit={onSubmitForm}>
<label htmlFor="username">
<FormattedMessage {...messages.trymeMessage} />
<AtPrefix>
<FormattedMessage {...messages.trymeAtPrefix} />
</AtPrefix>
<Input
id="username"
type="text"
placeholder="mxstbr"
value={username}
onChange={onChangeUsername}
/>
</label>
</Form>
<ReposList {...reposListProps} />
</Section>
</div>
</article>
);
}
HomePage.propTypes = {
loading: PropTypes.bool,
homeVisible: PropTypes.bool,
error: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]),
repos: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]),
onSubmitForm: PropTypes.func,
username: PropTypes.string,
onChangeUsername: PropTypes.func,
makeHomeVisible: PropTypes.func,
};
const mapStateToProps = createStructuredSelector({
repos: makeSelectRepos(),
username: makeSelectUsername(),
loading: makeSelectLoading(),
error: makeSelectError(),
homeVisible: makeHomeVisible(),
});
export function mapDispatchToProps(dispatch) {
return {
onChangeUsername: evt => dispatch(changeUsername(evt.target.value)),
makeHomeVisible: evt => dispatch(setHomeVisible(evt.target.value)),
onSubmitForm: evt => {
if (evt !== undefined && evt.preventDefault) evt.preventDefault();
dispatch(loadRepos());
},
};
}
const withConnect = connect(
mapStateToProps,
mapDispatchToProps,
);
export default compose(
withConnect,
memo,
)(HomePage);
As per the React Docs, you can use the useState hook to save data to state. There is no "state list or constructor" but you can simply access the props you pass in and decide to save those into state if they are going to be manipulated - if not stick to using the props.
Codesandbox Demo
Basic Example:
function Child(props) {
let [name, setName] = useState(props.data.name);
function updateName () {
setName('The Dude');
}
return (
<div className="App">
<h2>{name}</h2>
<button onClick={updateName}>Update</button>
</div>
);
};
function App() {
let [data, setData] = useState({name: 'dude'});
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<Child data={data}/>
</div>
);
}
I have a java script file called Toolbar.js in which I am trying to call a function that is in my App.js file as the following:
<DrawerToggleButton click={props.drawerClickHandler}/>
The above does not work, it shows "Unresolved variable drawerClickHandler" which I assume means that Toolbar.js can not see the functions in App.js. I have tried this without using props with no results. How can I get this to work?
Also just want to state the program will build and run with no errors, just that the button I am trying to map the function to, does nothing when I click it.
Code below:
Toolbar.js:
import React from 'react';
import './Toolbar.css';
import DrawerToggleButton from '../SideDrawer/DrawerToggleButton'
import '../SideDrawer/DrawerToggleButton';
import sideDrawer from "../SideDrawer/SideDrawer";
const toolbar = props =>(
<header className="toolbar">
<nav className="toolbar_navigation">
<div>
<DrawerToggleButton click={props.drawerClickHandler}/>
</div>
<div className="toolbar_logo">Kleen Portal</div>
<div className="spacer" />
<div className="toolbar_navigation-items">
<ul>
<li>Logout</li>
</ul>
</div>
</nav>
</header>
);
export default toolbar;
App.js:
import React, { useState, useEffect } from "react";
// import logo from './logo.svg';
import './App.css';
import Routes from "./Routes";
import { Auth } from "aws-amplify";
import { Link, withRouter } from "react-router-dom";
// import { Navbar } from "react-bootstrap";
import Toolbar from './components/Toolbar/Toolbar';
import SideDrawer from './components/SideDrawer/SideDrawer';
import Backdrop from './components/Backdrop/Backdrop';
function App(props) {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [isAuthenticating, setIsAuthenticating] = useState(true);
const [sideDrawerOpen, setIsSideDrawerOpen] = useState(false);
useEffect(() => {
onLoad();
}, [isAuthenticating, isAuthenticated]);
async function onLoad() {
try {
await Auth.currentSession();
setIsAuthenticated(true);
props.history.push("/dashboard");
}
catch(e) {
alert(e);
}
setIsAuthenticating(false);
}
function handleLogout() {
setIsAuthenticated(false);
props.history.push("/login");
}
function drawerToggleClickHandler(){
setIsSideDrawerOpen(!sideDrawerOpen)
}
let sideDrawer;
let backdrop;
let toolBar;
if(isAuthenticated){
toolBar = (
<Toolbar
handleLogout={handleLogout}
drawerClickHandler ={drawerToggleClickHandler}
/>
)
}
if (isAuthenticated && sideDrawerOpen){
sideDrawer = <SideDrawer/>;
backdrop = <Backdrop/>
}
return (
<div className="App container" style={{height: '100%'}}>
{toolBar}
{sideDrawer}
{backdrop}
{/*<Toolbar/>*/}
{/*<SideDrawer/>*/}
{/*<Backdrop/>*/}
<Routes appProps={{ isAuthenticated, setIsAuthenticated }} />
</div>
);
}
export default withRouter(App);
DrawerToggleButton.js:
import React from 'react';
import './DrawerToggleButton.css'
const drawerToggleButton = props => (
<button className="toggle-button" onClick={props.click}>
<div className="toggle-button_line" />
<div className="toggle-button_line" />
<div className="toggle-button_line" />
</button>
);
export default drawerToggleButton;
I'm new to React and I've made a <Link>to go to next or previous item from dy datas(for example, if i am on user/2 view, previous link go to user/1 and next link go to user/3), the url is correctly changed but the component is not rendered at all and the datas are not reloaded at all.
I've read that it's due to the component not detecting that the children is not changing state so the parent component does not render.
I've tried to use withRouter but I've got a error : You should not use <Route> or withRouter() outside a <Router> and I'm not understanding what I'm doing so if someone has the solution and some explanation to it I would be grateful :)
App.js :
import React, { Component } from 'react';
import {
Route,
Switch,
withRouter,
} from 'react-router-dom';
import HomePage from './pages/home';
import SinglePage from './pages/single';
class App extends Component {
render() {
return (
<Switch>
<div>
<Route exact path="/" component={HomePage} />
<Route path="/:id" component={SinglePage} />
</div>
</Switch>
);
}
}
export default withRouter(App);
Single.js :
import React, { Component } from 'react';
import Details from '../components/details'
import Header from '../components/header'
import { ProgressBar } from 'react-materialize';
class SinglePage extends Component {
constructor(props) {
super(props);
this.state = {
data: { data: null },
}
}
componentDidMount() {
fetch(`http://localhost:1337/${this.props.match.params.id}`)
.then((res) => res.json())
.then((json) => {
this.setState({
data: json,
});
});
}
render() {
const { data } = this.state;
return (
<div>
<h2> SinglePage </h2>
{!data ? (
<ProgressBar />
) : (
<div>
<Header id={this.props.match.params.id} />
<Details item={data} />
</div>
)}
</div>
);
}
}
export default SinglePage;
Header.js :
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Link, withRouter } from 'react-router-dom';
class Header extends Component {
static propTypes = {
item: PropTypes.shape({
data: PropTypes.string.isRequired,
}).isRequired,
}
render() {
const prev = parseInt(this.props.id) - 1
const next = parseInt(this.props.id) + 1
return (
<div>
<Link to="/"> Retour </Link>
<Link to={`/${prev}`}> Précédent </Link>
<Link to={`/${next}`}> Suivant </Link>
</div>
)
}
}
export default Header;
the solution is pretty-simple. All you need to do is make use of componentWillReceiveProps and check if the param updated, if it did fetch the data again
componentDidMount() {
this.getData(this.props.match.params.id);
}
componentWillReceiveProps(nextProps) {
if(this.props.match.params.id !== nextProps.match.params.id) {
this.getData(nextProps.match.params.id);
}
}
getData = (param) => {
fetch(`http://localhost:1337/${params}`)
.then((res) => res.json())
.then((json) => {
this.setState({
data: json,
});
});
}