I am learning React by making a motor cycle spec searching web application, and I am trying to import fetched data into makePicker.jsx to essentially make a drop down menu with those data.
However I am getting an error message saying:
Invalid hook call. Hooks can only be called inside of the body of a function component.
This could happen for one of the following reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
I am not sure what applies to my situation. Can you see why?
makePicker.jsx
import React, {useState, useEffect} from 'react';
import {NativeSelect, FormControl} from '#material-ui/core';
import styles from './makePicker.module.css';
import { makeList } from '../../api';
const MakePicker = ()=>{
const [fetchedMakes, setFetchedMakes] = useState([]);
useEffect(()=>{
const fetchAPI = async()=>{
setFetchedMakes(await makeList());
}
fetchAPI();
},[])
return (
<h1>makePicker</h1>
);
}
export default MakePicker;
App.js
import logo from './logo.svg';
import './App.css';
import {fetchData, makeList} from './api/index';
import React, {Component} from 'react';
import MakePicker from './components/makePicker/makePicker';
class App extends React.Component{
state = {
data:[],
makes:[],
}
async componentDidMount(){
// const fetchedData = await fetchData();
const fetchedMakeList = await makeList();
this.setState({makes:fetchedMakeList});
}
render(){
return (
<div className="App">
<header className="App-header">
{MakePicker()};
<h1>Some line-ups from YAMAHA</h1>
{this.state.makes.map(make=>{
return <p>{make.name}</p>
})}
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Open React
</a>
</header>
</div>
);
}
}
export default App;
Please let me know if need more information from my project.
According to your code in App.jsx and following the naming convention of React's component, you should import it with the first-capitalized-letter name like below
import MakePicker from './components/makePicker/makePicker';
You're also calling {makePicker()}; which is not a proper way for component rendering. You should modify it to
<MakePicker/>
Full possible change can be
import logo from './logo.svg';
import './App.css';
import {fetchData, makeList} from './api/index';
import React, {Component} from 'react';
import MakePicker from './components/makePicker/makePicker';
class App extends React.Component{
state = {
data:[],
makes:[],
}
async componentDidMount(){
// const fetchedData = await fetchData();
const fetchedMakeList = await makeList();
this.setState({makes:fetchedMakeList});
}
render(){
return (
<div className="App">
<header className="App-header">
<MakePicker/>
<h1>Some line-ups from YAMAHA</h1>
{this.state.makes.map(make=>{
return <p>{make.name}</p>
})}
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Open React
</a>
</header>
</div>
);
}
}
export default App;
To elaborate more why you get this error
Invalid hook call. Hooks can only be called inside of the body of a function component.
When you call makePicker(), it's actually triggering a Javascript's function (not React's component as you expected), but useEffect is only available in React's components. That's why when you try to run code, the compiler is thinking makePicker is calling useEffect invalidly.
Their is a problem lies on your useEffect
update the code like this and try it out:
const fetchAPI = async()=>{
const res = await makeList().then((data) => {
setFetchedMakes(data)
}).catch((err) => console.log(err))
}
useEffect(()=>{
fetchAPI();
},[])
here i placed the asynchronous function outside of the useEffect
Also make MakePicker as an JSX element like this: <MakePicker />
Related
i have been trying to implement a context api solution since i want to use children states(data) in my app.js without lifting up the states. anyways i have tried to implement it a context api soloution to by doing the following :
i created a folder names context and then created Context.js
the code is as follows:
mport { createContext,useState } from "react";
export const Mycontext = createContext()
const Context = ({children}) =>{
const [post, setPost] = useState([])
return(
<Mycontext.Provider value={[post,setPost]}>
{children}
</Mycontext.Provider>
)
}
export default Context
i wrapped the index.js file with the Provider wrapper as follows:
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import Context from './context/Context';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Context>
<App />
</Context>
);
my main goal for now is to use useState hook data or states so i can use them in higher up comonents , in this case i want my post.js file to change usestate data in context so i can then use that data to post something in App.js using a container component that takes value as a props
i will post the both post.js and container.js and app.js below
import React,{useContext,useState,useEffect,useRef} from 'react'
import '../HomeMainStyling/HomeStyling.css'
import Tweets from './Tweets'
import Context from '../../context/Context'
function Tweet() {
const tw = useRef('')
const {post,setPost} = useContext(Context);
useEffect(() => {
if (post.length) console.log(post);
}, [post]);
function PostNow(event){
event.preventDefault();
setPost((oldpost) => [tw.current.value,...oldpost]);
}
return (
<div className="tweetcontainer">
<textarea ref={tw} className="tweetinfo"></textarea>
<button className="postIt" onClick={PostNow}>tweet</button>
</div>
)
}
export default Tweet
//
the container is the following:
import React from 'react'
import '../HomeMainStyling/HomeStyling.css'
function Tweets({value}) {
return (
<h2>{value}</h2>
)
}
export default Tweets
App.js:
import Tweet from './Center/HomeMain/Tweet';
import Tweets from './Center/HomeMain/Tweets';
import { useContext,useState } from 'react';
import Context from './context/Context';
function App() {
const {post,setPost} = useContext(Context);
return (
<div className="App">
<Tweet/>
<Tweets value={post}/>
</div>
);
}
export default App;
the app should in principle post 1 h1 element for every click in Tweet components
The useContext hook takes the context you created using createContext() as a parameter, but you are passing a custom component to it, so try:
import { Mycontext } from './context/Context';
const [post, setPost] = useContext(Mycontext)
<Mycontext.Provider value={[post,setPost]}>
this is wrong you ahve to write
<Mycontext.Provider value={{post,setPost}}>
I am making a simple blog post site where user can post an entry.
The objective is:
To have the Homepage display all the blog post in a list and details of any one blog post next to it when the Homepage first loads. When the user clicks on any item from the list the details of which will replace the default blog details.
Basically how Indeed displays the job posts.
Method:
I make 2 Axios calls one to get all the data and the other to get data by id.
The data from getAllData is displayed in a list on HomePage The data is passed as props to HomePageListItems which are wrapped in <Link to= {/${idx}}/>
I use the useParams to get the id and make getDataId call in DataById.
So, HomePage has to child components HomePageListItems and DataById
Issue is:
Function getDataById in does not work when Homepage is first loaded. It only works when route is "localhost/:id" which is "localhost:3000/ae86140b-7ae6-457-826c-5bd324b8cb3"
Because I want one blog already displayed when the first loads: How I do have this getDatabyId function run with a preset id where the id changes when the user clicks on a list item?
The code is:
import React, { useState, useEffect, usePrevious } from "react";
import { Link, useParams } from "react-router-dom";
import Axios from "axios";
import HomePageListItems from "./homePageListItems";
import DataById from "./dataById";
export default function HomePage(props){
<DataById/>
const [getAllData, setGetAllData] = useState()
const getData =async () => {
await Axios.get("http://localhost:5000/get").then((response)=>{
setGetAllData(response.data)
})
.catch((error)=>{console.log(error)})
}
useEffect(()=>{
getData();
},[])
return(
<section>
<Link to = "/postJournal">Post Journal Entry</Link>
{getAllData&&getAllData.map((item)=>{
return(
<HomePageListItems
key={item.id}
idx={item.id}
name={item.name}
title={item.title}
journalEntry={item.journalEntry}
date={item.date}
file={item.file}
/>
)
})
}
{usePrevious}
<DataById/>
</section>
)
}
--
import React, { useState, useEffect, usePrevious } from "react";
import { useParams } from "react-router-dom";
import Axios from "axios";
export default function DataById(props){
console.log("DataProps",props)
const [axiosGetData, setAxiosGetData] = useState([])
const {id} = useParams()
const getDataById =async () => {
await Axios.get(`http://localhost:5000/${id}`).then((response)=>{
setAxiosGetData(response.data[0])
console.log("Data",response.data[0])
})
.catch((error)=>{console.log(error)})
}
useEffect(()=>{
getDataById("35b48be0-0ab8-4409-a5eb-a0c4dbd0a4b3");
},[id])
return(
<>
<p> DataByID</p>
name:{axiosGetData.name}
Date:{axiosGetData.date}
Journal:{axiosGetData.journalEntry}
{usePrevious}
</>
)
}
--
import React, { useState, useEffect, usePrevious} from "react";
import { Link} from "react-router-dom";
export default function HomePageListItems(props){
let {name,title,journalEntry,date,file,idx}=props
return(
<main className= "homepage">
<Link to={`/${idx}`} >
<section className="homepage__listContainer">
<ul className = "homepage__list">
<li className="homepage__listItemWrapper">
<div className ="homepage__listItem">
<div className = "homepage__listItemImgWrapper">
<img className = "homepage__listItemImg" alt="" src={file}/>
</div>
<h3 className= "homepage__listItemTitle">Title:{title}</h3>
<p className = "homepage__listItemAuthor">Name:{name}</p>
<p className = "homepage__listItemDescription">Description:{journalEntry}</p>
<p className = "homepage__listItemDate">Posted Date:{date}</p>
<p>Key:{idx}</p>
</div>
</li>
</ul>
</section>
</Link>
{usePrevious}
</main>
)
}
--
import React from "react";
import "./style/App.css";
import ReactDOM from 'react-dom';
import { BrowserRouter, Switch, Router, Route } from "react-router-dom";
import HomePage from "./components/homePage"
import PostJournalEntry from "./components/postJournalEntry"
import DataByIDfrom "./components/dataById"
function App() {
return (
<div className="app">
<BrowserRouter>
<Switch>
<Route path="/:id" exact component = {HomePage}/>
<Route path="/:id" exact component = {DataByID}/>
<Route path="/postJournal" exact component = {PostJournalEntry}></Route>
<Route path="/" exact component = {HomePage}/>
</Switch>
</BrowserRouter>
</div>
);
}
export default App;
Thanks in advance guys! Any help is appriciated.
It's because getDataById doesn't accept any arguments, it always uses the URL id (useParams) for the axios get call, passing it an id in the useEffect won't do anything. You need to add a parameter to the function and then add some logic so it knows whether to use a passed in value or the URL id value. You could try something like this:
const getDataById = async (startupId = null) => {
await Axios.get(`http://localhost:5000/${startupId ? startupId : id}`).then((response)=>{
setAxiosGetData(response.data[0])
console.log("Data",response.data[0])
})
.catch((error)=>{console.log(error)})
}
This way if you pass getDataById an argument it will use that value for the axios call, otherwise it will try to use the id value from useParams
I need to create a React app which let's you list pokemons and types.
I fetch data from the PokeAPI. Is it a good practice to fetch it from the App component and then pass it to the child components, or is it better to fetch them from the child?
I am fetching it in the main app, I can see the fetch works because I console.log the data, but my component doesn't get it, and because of that I get a props.map is not a function in .
Here is my App.js:
import React, { useState } from "react";
import logo from "./logo.svg";
import "./App.css";
import axios from "axios";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import PokemonList from "./components/PokemonList";
const App = (props) => {
const [pokemons, setPokemons] = useState([]);
const [types, setTypes] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const getPokemons = () => {
const axios = require("axios").default;
axios.get("https://pokeapi.co/api/v2/pokemon").then(function (response) {
console.log("Fetched pokemons");
console.log(response.data.results);
setIsLoading(false);
setPokemons(response.data.results);
});
};
const getTypes = () => {
setIsLoading(true);
const axios = require("axios").default;
axios.get("https://pokeapi.co/api/v2/type").then(function (response) {
console.log("Fetched types");
console.log(response.data.results);
setIsLoading(false);
setTypes(response.data.results);
});
};
return (
<Router>
<div>
<nav>
<ul>
<li>
<Link to="/pokemons" onClick={getPokemons}>
Pokemons
</Link>
</li>
<li>
<Link to="/types">Types</Link>
</li>
</ul>
</nav>
{/* A <Switch> looks through its children <Route>s and
renders the first one that matches the current URL. */}
<Switch>
<Route path="/pokemons">
<Pokemons pokemons={pokemons} />
</Route>
<Route path="/types">
<Types />
</Route>
</Switch>
</div>
</Router>
);
};
function Pokemons(pokemons) {
return <PokemonList props={pokemons} />;
}
function Types(typeList) {
return <h2>TYPES:</h2>;
}
export default App;
Here is my PokemonList.js:
import React from "react";
import { Card } from "semantic-ui-react";
import PokeCard from "./PokeCard";
const Pokemonlist = (props) => {
let content = (
<Card.Group>
{props.map(function (object, i) {
return <PokeCard pokemon={object} key={i} />;
})}
</Card.Group>
);
return content;
};
export default Pokemonlist;
and last here is my PokeCard.js
import { Card, Image } from "semantic-ui-react";
import React from "react";
const PokeCard = (pokemon) => {
let content = (
<Card>
<Card.Content>
<Image floated="right" size="mini" src={pokemon.img} />
<Card.Header>{pokemon.name}</Card.Header>
<Card.Meta>{pokemon.base_experience}</Card.Meta>
<Card.Description>ID: {pokemon.id}</Card.Description>
</Card.Content>
</Card>
);
return content;
};
export default PokeCard;
So the basic idea is:
On the main page you click Pokemons button, which calls the fetch then renders the PokemonList component which basically just renders multiple PokeCard components from the data I fetched.
1, What am I missing here?
2, In my situation when nothing changes do I need to use useEffect?
3, When should I fetch the data, and where?
EDIT: I want to use hooks with zero classes
here is a summary of my answer
it is best to fetch some initial data in parent and then make further requests in child
component if necessary to save network usage
use the useEffect hook to fetch the results before rendering the elements
What you are missing is that you are not using props in pokemon and you should put the get call inside useEffect hook in App component because the child component is rendering before the props is passed to it and this is the reason you are getting undefined error
I have implemented mgt-login component from Microsoft Graph toolkit, it is working fine but it's not calling event I added to mgt-login within useEffect. Duplicate question here - I did follow this question but still its not calling event I added. Here is code for that component
import React, {
useRef,
useEffect,
} from 'react';
const Login = () => {
const loginComponent = useRef(null);
useEffect(() => {
loginComponent.current.addEventListener('loginCompleted', () => console.log('Logged in!'));
}, []);
return (
<div className="login">
<mgt-login ref={loginComponent} />
</div>
);
};
Here is how instantiate Provider in main index.jsx file of app
import { Providers, MsalProvider } from '#microsoft/mgt';
Providers.globalProvider = new MsalProvider({
clientId: process.env.REACT_APP_DEV_AZURE_APP_CLIENT_ID,
});
I don't know what I am missing, not sure if something was updated about this component(BTW, I have not found any change in microsoft graph docs).
Thanks!
To make the use of mgt components in React easier, we created a React wrapper that you can use here, mgt-react.
import React, {
useRef,
useEffect,
} from 'react';
import {Login} from '#microsoft/mgt-react';
const Login = () => {
return (
<div className="login">
<Login loginCompleted={(e) => console.log('Logged in')} />
</div>
);
};
Trying to figure out how can I go back to the previous page. I am using [react-router-v4][1]
This is the code I have configured in my first landing page:
<Router>
<div>
<Link to="/"><div className="routerStyle"><Glyphicon glyph="home" /></div></Link>
<Route exact path="/" component={Page1}/>
<Route path="/Page2" component={Page2}/>
<Route path="/Page3" component={Page3}/>
</div>
</Router>
In order to forward to subsequent pages, I simply do:
this.props.history.push('/Page2');
However, how can I go back to previous page?
Tried few things like mentioned below but no luck:
1. this.props.history.goBack();
Gives error:
TypeError: null is not an object (evaluating 'this.props')
this.context.router.goBack();
Gives error:
TypeError: null is not an object (evaluating 'this.context')
this.props.history.push('/');
Gives error:
TypeError: null is not an object (evaluating 'this.props')
Posting the Page1 code here below:
import React, {Component} from 'react';
import {Button} from 'react-bootstrap';
class Page1 extends Component {
constructor(props) {
super(props);
this.handleNext = this.handleNext.bind(this);
}
handleNext() {
this.props.history.push('/page2');
}
handleBack() {
this.props.history.push('/');
}
/*
* Main render method of this class
*/
render() {
return (
<div>
{/* some component code */}
<div className="navigationButtonsLeft">
<Button onClick={this.handleBack} bsStyle="success">< Back</Button>
</div>
<div className="navigationButtonsRight">
<Button onClick={this.handleNext} bsStyle="success">Next ></Button>
</div>
</div>
);
}
export default Page1;
I think the issue is with binding:
constructor(props){
super(props);
this.goBack = this.goBack.bind(this); // i think you are missing this
}
goBack(){
this.props.history.goBack();
}
.....
<button onClick={this.goBack}>Go Back</button>
As I have assumed before you posted the code:
constructor(props) {
super(props);
this.handleNext = this.handleNext.bind(this);
this.handleBack = this.handleBack.bind(this); // you are missing this line
}
UPDATED:
Now we have hook, so we can do it easily by using useHistory
const history = useHistory()
const goBack = () => {
history.goBack()
}
return (
<button type="button" onClick={goBack}>
Go back
</button>
);
ORIGINAL POST:
this.props.history.goBack();
This is the correct solution for react-router v4
But one thing you should keep in mind is that you need to make sure this.props.history is existed.
That means you need to call this function this.props.history.goBack(); inside the component that is wrapped by < Route/>
If you call this function in a component that deeper in the component tree, it will not work.
EDIT:
If you want to have history object in the component that is deeper in the component tree (which is not wrapped by < Route>), you can do something like this:
...
import {withRouter} from 'react-router-dom';
class Demo extends Component {
...
// Inside this you can use this.props.history.goBack();
}
export default withRouter(Demo);
For use with React Router v4 and a functional component anywhere in the dom-tree.
import React from 'react';
import { withRouter } from 'react-router-dom';
const GoBack = ({ history }) => <img src="./images/back.png" onClick={() => history.goBack()} alt="Go back" />;
export default withRouter(GoBack);
Each answer here has parts of the total solution. Here's the complete solution that I used to get it to work inside of components deeper than where Route was used:
import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'
^ You need that second line to import function and to export component at bottom of page.
render() {
return (
...
<div onClick={() => this.props.history.goBack()}>GO BACK</div>
)
}
^ Required the arrow function vs simply onClick={this.props.history.goBack()}
export default withRouter(MyPage)
^ wrap your component's name with 'withRouter()'
Here is the cleanest and simplest way you can handle this problem, which also nullifies the probable pitfalls of the this keyword. Use functional components:
import { withRouter } from "react-router-dom";
wrap your component or better App.js with the withRouter() HOC this makes history to be available "app-wide". wrapping your component only makes history available for that specific component``` your choice.
So you have:
export default withRouter(App);
In a Redux environment export default withRouter( connect(mapStateToProps, { <!-- your action creators -->})(App), ); you should even be able to user history from your action creators this way.
in your component do the following:
import {useHistory} from "react-router-dom";
const history = useHistory(); // do this inside the component
goBack = () => history.goBack();
<btn btn-sm btn-primary onclick={goBack}>Go Back</btn>
export default DemoComponent;
Gottcha useHistory is only exported from the latest v5.1 react-router-dom so be sure to update the package. However, you should not have to worry.
about the many snags of the this keyword.
You can also make this a reusable component to use across your app.
function BackButton({ children }) {
let history = useHistory()
return (
<button type="button" onClick={() => history.goBack()}>
{children}
</button>
)
}```
Cheers.
Can you provide the code where you use this.props.history.push('/Page2');?
Have you tried the goBack() method?
this.props.history.goBack();
It's listed here https://reacttraining.com/react-router/web/api/history
With a live example here https://reacttraining.com/react-router/web/example/modal-gallery
If using react hooks just do:
import { useHistory } from "react-router-dom";
const history = useHistory();
history.go(-1);
UPDATE 2022 w V6
navigate(-1)
to omit the current page from history:
navigate(-1, { replace: true })
Try:
this.props.router.goBack()
Simply use
<span onClick={() => this.props.history.goBack()}>Back</span>
Hope this will help someone:
import React from 'react';
import * as History from 'history';
import { withRouter } from 'react-router-dom';
interface Props {
history: History;
}
#withRouter
export default class YourComponent extends React.PureComponent<Props> {
private onBackClick = (event: React.MouseEvent): void => {
const { history } = this.props;
history.goBack();
};
...
Maybe this can help someone.
I was using history.replace() to redirect, so when i tried to use history.goBack(), i was send to the previous page before the page i was working with.
So i changed the method history.replace() to history.push() so the history could be saved and i would be able to go back.
I am not sure if anyone else ran into this problem or may need to see this. But I spent about 3 hours trying to solve this issue:
I wanted to implement a simple goBack() on the click of a button. I thought I was off to a good start because my App.js was already wrapped in the Router and I was importing { BrowserRouter as Router } from 'react-router-dom'; ... Since the Router element allows me to assess the history object.
ex:
import React from 'react';
import './App.css';
import Splash from './components/Splash';
import Header from './components/Header.js';
import Footer from './components/Footer';
import Info from './components/Info';
import Timer from './components/Timer';
import Options from './components/Options';
import { BrowserRouter as Router, Route } from 'react-router-dom';
function App() {
return (
<Router>
<Header />
<Route path='/' component={Splash} exact />
<Route path='/home' component={Info} exact />
<Route path='/timer' component={Timer} exact />
<Route path='/options' component={Options} exact />
<Footer />
</Router>
);
}
export default App;
BUT the trouble was on my Nav (a child component) module,
I had to 'import { withRouter } from 'react-router-dom';'
and then force an export with:
export default withRouter(Nav);
ex:
import React from 'react';
import { withRouter } from 'react-router-dom';
class Nav extends React.Component {
render() {
return (
<div>
<label htmlFor='back'></label>
<button id='back' onClick={ () => this.props.history.goBack() }>Back</button>
<label htmlFor='logOut'></label>
<button id='logOut' ><a href='./'>Log-Out</a>
</button>
</div>
);
}
}
export default withRouter(Nav);
in summary, withRouter was created because of a known issue in React where in certain scenarios when inheritance from a router is refused, a forced export is necessary.
You can use history.goBack() in functional component. Just like this.
import { useHistory } from 'react-router';
const component = () => {
const history = useHistory();
return (
<button onClick={() => history.goBack()}>Previous</button>
)
}