React.js - Functions are not valid as a React child - javascript

I am new to React.js. I can't solve the problem. I am getting this warning:
Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.
App.js
`
import React from 'react';
import MovieList from './MovieList';
import SearchBar from './SearchBar';
import AddMovie from './AddMovie';
import axios from 'axios'
import { BrowserRouter as Router, Routes, Route } from "react-router-dom"
class App extends React.Component {
state = {
movies: [],
searchQuery: ""
}
async componentDidMount() {
const response = await axios.get("http://localhost:3002/movies")
this.setState({movies: response.data})
}
deleteMovie = async (movie) => {
axios.delete(`http://localhost:3002/movies/${movie.id}`)
const newMovieList = this.state.movies.filter(
m => m.id !== movie.id
)
this.setState(state => ({
movies: newMovieList
}))
}
searchMovie = (event) => {
this.setState({searchQuery: event.target.value })
}
render() {
let filteredMovies = this.state.movies.filter(
(movie) => {
return movie.name.toLowerCase().indexOf(this.state.searchQuery.toLowerCase()) !== -1
}
)
return (
<Router>
<div className="container">
<Routes>
<Route path='/' exact element={() =>(
<React.Fragment>
<div className="row">
<div className="col-lg-12">
<SearchBar const searchMovieProp={this.searchMovie()} />
</div>
</div>
<MovieList
movies={filteredMovies()}
deleteMovieProp={this.deleteMovie()}
/>
</React.Fragment>
)}>
</Route>
<Route path='/add' element={<AddMovie />} />
</Routes>
</div>
</Router>
)
}
}
export default App;
`
What am I doing wrong?
Thanks in advance.

Passing a function to a route like you did:
<Route path='/' exact element={() =>(
<React.Fragment>
<div className="row">
<div className="col-lg-12">
<SearchBar const searchMovieProp={this.searchMovie()} />
</div>
</div>
<MovieList
movies={filteredMovies()}
deleteMovieProp={this.deleteMovie()}
/>
</React.Fragment>
)}>
looks like a router v5 syntax. This is not working in v6: you should pass an element, which is different than a function producing an element. Something like this would work:
<Route path='/' exact element={(
<React.Fragment>
<div className="row">
<div className="col-lg-12">
<SearchBar const searchMovieProp={this.searchMovie()} />
</div>
</div>
<MovieList
movies={filteredMovies()}
deleteMovieProp={this.deleteMovie()}
/>
</React.Fragment>
)}>

Related

React pass fetched data from API to another component

I am fetching few products from an API, and displaying them in card. There is a More Details link on the cards, where if the user clicks on it, it will take the user to the selected product details page. My routing to productDetails page works, But I am having troubles to find a way to pass the fetched data to the productDetails page as props.
This is what I have so far:
My FeaturedProduct.js:
import React from "react";
import { useState, useEffect } from "react";
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
import ProductDetails from "./ProductDetails";
import axios from "axios";
function FeaturedProduct(props) {
const [products, setProducts] = useState([]);
useEffect(() => {
fetchProducts();
}, []);
function fetchProducts() {
axios
.get("https://shoppingapiacme.herokuapp.com/shopping")
.then((res) => {
console.log(res);
setProducts(res.data);
})
.catch((err) => {
console.log(err);
});
}
return (
<div>
<h1> Your Products List is shown below:</h1>
<div className="item-container">
{products.map((product) => (
<div className="card" key={product.id}>
{" "}
<h3>{product.item}</h3>
<p>
{product.city}, {product.state}
</p>
<Router>
<Link to="/productdetails">More Details</Link>
<Switch>
<Route path="/productdetails" component={ProductDetails} />
</Switch>
</Router>
</div>
))}
</div>
</div>
);
}
export default FeaturedProduct;
My Product Details Page:
import React from "react";
import FeaturedProduct from "./FeaturedProduct";
function ProductDetails(props) {
return (
<div>
<div>
<h1>{props.name}</h1>
<h1>{props.color}</h1>
</div>
</div>
);
}
export default ProductDetails;
I am still learning but this is what I would do:
<Route path="/productdetails">
<ProductDetails product={product}/>
</Route>
====
On ProductDetails you can destructure the props:
function ProductDetails(props) {
const {name, color} = props.product;
return (
<div>
<div>
<h1>{name}</h1>
<h1>{color}</h1>
</div>
</div>
);
}
export default ProductDetails;
Pass it as an element with props, if you are using v 6; sorry I didn't ask which version. >
<Switch>
<Route path="/productdetails" element={<ProductDetails {...props} />}/>
</Switch>
if version v4/5 use the render method >
<Route path="/productdetails" render={(props) => (
{ <ProductDetails {...props} />} )}/>
//pass it this way
<Switch>
<Route
path="/productdetails"
render={() => (
{ <ProductDetails product={product}/>})}/>
/>
</Switch>

How to pass data from a child to another a child (nested in Home page) in React?

I'm struggling to figure out how to pass the search term from ChildOne to ChildTwo (which is nested in a page). I hope all the code I provided down below will make it clear. I tried to lift up the state to the App.js component but it didn't work or maybe I didn't do it correctly. I would appreciate any help. Thanks in advance :)
Child 1:
const ChildOne = () => {
const [searhTerm, setSearchTerm] = useState("");
return(
<InputContainer>
<input
type="text"
placeholder="Find a recipe"
value={searchTerm}
onChange={(e) => {
setSearchTerm(e.target.value);
}}
/>
<SearchIcon />
</InputContainer>
)
}
Child 2:
const ChildTwo = () => {
// I want to pass the searchTerm to be used in a fetch request in this component
const apiURL = `'url' + {searchTerm}`;
return(
...
)
}
App.js
function App(){
return(
<>
<ChildOne/>
<Switch>
<Route path="/" exact component={Home}/>
<Switch/>
</>
)
}
Home.js:
const Home = () => {
return (
<>
<ChildTwo />
</>
);
};
there is several way to do that...
I suggest you use Context Api.
if you don't want to use Context Api or State management
see this example
enter link description here
import { useState } from "react";
import {
Route,
Switch,
BrowserRouter as Router,
RouterProps
} from "react-router-dom";
import ChildOne from "./ChildOne";
import Home from "./Home";
function App() {
const [value, setValue] = useState("");
return (
<>
<ChildOne setValue={setValue} />
<Router>
<Switch>
<Route path="/" exact>
<Home value={value} />
</Route>
</Switch>
</Router>
</>
);
}
export default App;

Props passed through React Route cant be accessed by child component

I am having a problem accessing the props passed through the route to the child component.
I am trying to get hold of the Authentication function in the App page , so that I can toggle it to true when my onLogin function in the Login page get the correct response.
Any help will be highly appreciated.
please find the code below
//App.js
import React, { Component } from "react";
//import TopNavigation from './components/topNavigation';
//import { BrowserRouter, Route, Switch } from "react-router-dom";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import MainPage from "./Mainpage";
import LoginPage from "./components/pages/LoginPage";
//import ProtectedRoute from "./protected.route";
import "./styleFiles/index.css";
class App extends Component {
constructor() {
super();
this.state = {
isAuthenticated: false
};
this.Authentication = this.Authentication.bind(this);
}
Authentication(e) {
this.setState({ isAuthenticated: e });
}
render() {
if (this.state.isAuthenticated === false) {
return (
<BrowserRouter>
<div className="flexible-content ">
<Route
path="/login"
render={props => <LoginPage test="helloworld" {...props} />}
/>
<Route component={LoginPage} />
</div>
</BrowserRouter>
);
} else {
return (
<div className="flexible-content ">
<Switch>
<Route
path="/"
exact
component={MainPage}
/>
<Route component={MainPage} />
</Switch>
</div>
);
}
}
}
export default App;
//login page
import React, { Component } from "react";
import logo from "../../assets/justLogo.svg";
import "../../styleFiles/loginCss.css";
class LoginPage extends Component {
constructor(props) {
super(props);
}
onLogin = event => {
let reqBody = {
email: "rpser1234#gmail.com",
password: "rpser1234"
};
// event.preventDefault();
fetch("http://localhost:8000/api/auth/login", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(reqBody)
})
.then(res => {
if (res.ok) {
return res.json();
} else {
throw new Error("Something went wrong with your fetch");
}
})
.then(json => {
localStorage.setItem("jwtToken", json.token);
console.log(this.props.test);
});
};
render() {
const {
test,
match: { params }
} = this.props;
console.log(test);
return (
<div className="bodybg">
<div className="login-form">
<div className="form-header">
<div className="user-logo">
<img alt="MDB React Logo" className="img-fluid" src={logo} />
</div>
<div className="title">Login</div>
</div>
<div className="form-container">
<div className="form-element">
<label className="fa fa-user" />
<input type="text" id="login-username" placeholder="Username" />
</div>
<div className="form-element">
<label className="fa fa-key" />
<input type="text" id="login-password" placeholder="Password" />
</div>
<div className="form-element">
<button onClick={this.onVerify}>verify</button>
</div>
<div className="form-element forgot-link">
Forgot password?
</div>
<div className="form-element">
<button onClick={this.onLogin}>login</button>
</div>
</div>
</div>
</div>
);
}
}
export default LoginPage;
I am trying to access the test prop from the loginPage but no success.
Any idea were am I going wrong?
Try this:
<Route
path="/login"
render={props => <LoginPage test={this.state.isAuthenticated} {...props} />}
/>
You have isAuthenticated not Authentication.
You are using state to pass function as prop,
<Route
path="/login"
render={props => <LoginPage test={this.state.Authentication} {...props} />} //Wrong way to pass function as prop
/>
If you want to pass a function as prop use this, Ref
<Route
path="/login"
render={props => <LoginPage test={this.Authentication} {...props} />}
/>
If you want multiple routes for same component, then use exact attribute like this, check this
<Route
exact //This will match exact path i.e. '/login'
path="/login"
render={props => <LoginPage test={this.Authentication} {...props} />}
/>
Your approach is not the best practice. For such purposes it is best to use redux + redux-thunk.
If you still want to go this way.
You make mistake on here:
<Route
path="/login"
render={props => <LoginPage test={this.state.isAuthention} {...props} />}
/>
this.state.isAuthention replace on this.state.isAuthenticated
After that you need send via props Authentication callback and call it's in fetch( ... ).then((result) => this.props.Authentication(result))

react-router-dom Redirect causes : Maximum update depth exceeded

I am attempting to redirect from the root url of my app and I get the above error. I have read some of the other answers here on SO that reference this error but they center on state being updated cyclically and I can't see where I'm doing it in the Router:
import React from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter as Router, Route, Switch, Redirect} from 'react-router-dom'
import BookDetails from './BookDetails'
import NavBar from './NavBar'
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(
<>
<Router>
<NavBar/>
<Switch>
<Redirect from="/" to="/search" component={App} /> {/** */}
<Route path="/search" component={App} />
<Route path="/" component={App} />
</Switch>
<Route path="/book/:bookId" component={BookDetails} />
</Router>
</>
, document.getElementById('root'));
Or in the App.js file which I hope is okay :
import React, {useState, useEffect} from 'react'
import {Link} from 'react-router-dom'
// import debounce from 'lodash'
import noimage from './noimage.png'
import axios from 'axios'
import './App.css';
const App = (props) =>{
const [books,setBooks] = useState([])
const [isLoading,setIsLoading] = useState(false)
const [hasError,setHasError] = useState(false)
const [searchTerm,setSearchTerm] = useState('')
console.log("props",(props.history.location.pathname))
const input = str => {
console.log(str)
setSearchTerm(str)
getData(str)
}
const getData = async () => {
if(searchTerm && searchTerm.length >= 2){
if(isLoading === false)setIsLoading(true)
let s = `http://localhost:7000/books/search/${searchTerm}`
await axios.get(s)
.then(resp => {
console.log(resp.data.books)
if(books === [])setBooks([...resp.data.books])
//props.history.push(s)
})
.catch(error => {
console.log(error)
setHasError(true)
setIsLoading(false)
})
}
if(isLoading === true) setIsLoading(false)
}
const img = {
height : "175px",
width : "175px"
}
// useEffect(() =>{
// setTimeout(() => getData(),2250)
// },[])
return isLoading ? (
<div className="App">
<div className="spinner"></div>
<div className="App loading">
<p><i>loading...</i></p>
</div>
</div>
)
: hasError ? (
<div className="App loading-error">
⚠ There is a network issue: Please try again later
</div>
)
:
(
<div className="App">
<div className="search">
<div className="container">
<div className="content">
<input
type="search"
placeholder="Search..."
aria-label="Search"
className="search-form"
value={searchTerm}
onChange={e => input(e.target.value)}
/>
</div>
</div>
</div>
{
(books && books.length >= 1) &&
books.map((b,i) => {
console.log(typeof b.imageLinks)
return (
<div key={`${b.title}-${i}`} className="search-book">
<Link to={`/book/${b.id}`}>
{
(b.imageLinks === undefined || b === undefined || b.imageLinks.thumbnail === undefined) ?
<img src={noimage} alt ="Missing" style={img}></img>
:
<img src={b.imageLinks.thumbnail} alt={b.title}></img>
}
</Link>
<p>{b.title}</p>
<hr/>
</div>
)}
)
}
</div>
)
}
export default App;
I can't demo the code since it involves a REST Api server that's local to my machine. Thanks.
Well my instructor suggested this : https://reacttraining.com/react-router/web/api/Switch
and I thought I tried this already but I read the docs and tried this -
(
<Router>
<NavBar/>
<Switch>
<Redirect exact from="/" to="/search" component={App} />
<Route path="/search" component={App} />
<Route path="/book/:bookId" component={BookDetails} />
</Switch>
</Router>
)
The exact property isn't needed on all the routes. But I thank Kishan for his effort, he definitely was on the right track. Thanks again.
<>
<Router>
<NavBar/>
<Switch>
<Redirect from="/" to="/search" component={App} /> {/** */}
<Route exact path="/search" component={App} />
<Route exact path="/book/:bookId" component={BookDetails} />
</Switch>
</Router>
</>
try this way, maybe helps you

<Link> doesn't update component using React + Redux (not a dupe)

guys, first of all, it may look like a duplicate, but I swear, I searched all over the place and all the possible solutions didn't worked for me.
The problem is the classic "When I click on a element the URL changes but the component doesn't update"
In fact, it updates, but only on the first click.
Here is my relevant code:
index.js
...
render(
<Provider store={store}>
<ConnectedRouter history={history}>
<div>
<App />
</div>
</ConnectedRouter>
</Provider>,
target
);
App component
...
const App = () => (
<BrowserRouter>
<div>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/items/:id" component={Product} />
<Route path="/items" component={Search} />
</Switch>
</div>
</BrowserRouter>
);
export default App;
Search component
...
const Search = props => (
<div>
<header>
<SearchBar />
</header>
<main>
<SearchResult />
</main>
</div>
);
export default Search;
SearchResult component
const renderItem = result => (
<div key={result.id} className="result-item">
<Link to={`/items/${result.id}`}>
...
</Link>
</div>
);
class SearchResult extends Component {
componentWillMount() {
if (!this.props.resultIsLoading && !this.props.results.items) {
const { search: term } = queryString.parse(this.props.location.search);
this.props.search(term);
}
}
render() {
if (!this.props.results.items) return <div>...</div>;
return (
<div className="container">
<div className="product-list">
{this.props.results.items.map(renderItem)}
</div>
</div>
);
}
}
const mapStateToProps = ({ results, resultIsLoading }) =>
({ results, resultIsLoading });
const mapDispatchToProps = dispatch =>
bindActionCreators({ search }, dispatch);
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(SearchResult));
When I click the , the state doesn't change, the reducer is not called and the ProductDetails component doesn't update.
I've tried a lot of possible solutions. { pure: false } on connect, pass location as a prop, remove main BrowserRouter, remove withRouter, none seems to work.
Can, please, someone give me a light on this?

Categories