having some trouble rendering props in a component for a project using Reactjs. The information is showing in props in the react dev tools, yet I am unable to render them on the browser. When console logging, there is no value showing...
I'm wondering if I need to dig deeper into the api in order to grab what I need?
CocktailRecipe.js
````import React, { Component } from 'react'
// import Spinner from '../layout/Spinner'
class CocktailRecipe extends Component {
componentDidMount(){
this.props.getCocktail(this.props.match.params.idDrink);
// console.log(this.props.match.params.idDrink)
}
render() {
const {strDrink} = this.props.cocktailrecipe;
console.log(strDrink);
// console.log(this.props.cocktailrecipe.strDrink);
// const {loading} = this.props.loading;
// if (loading) {
// <Spinner />
// }else{
return (
<div>
<h3>{strDrink}</h3>
<h3>This is the title</h3>
</div>
)
// }
}
}
export default CocktailRecipe````
app.js
````import { Component, Fragment } from 'react';
import { BrowserRouter as Router, Switch, Route} from 'react-router-dom';
import './App.css';
import Navbar from './layout/Navbar';
import CocktailList from './cocktail/CocktailList';
import axios from 'axios';
import Search from './cocktail/Search';
import Alert from './layout/Alert';
import About from './pages/About';
import CocktailRecipe from './cocktail/CocktailRecipe';
class App extends Component {
state={
cocktails: [],
cocktailrecipe:{},
loading: false,
msg:'',
type:''
}
async componentDidMount() {
try {
this.setState({loading: true})
const res = await axios.get('https://www.thecocktaildb.com/api/json/v1/1/search.php?s=');
// console.log(res.data);
this.setState({cocktails: res.data.drinks, loading: false})
} catch(error) {
console.log(error)
}
}
handleSearchCocktails= async (text) => {
try{
const res = await axios.get(`https://www.thecocktaildb.com/api/json/v1/1/search.php?s=${text}`);
// console.log(res.data);
this.setState({cocktails: res.data.drinks, loading: false})
} catch(error) {
console.log(error)
}
}
// Get cocktail recipe
getCocktail = async (idDrink) => {
try {
const res = await axios.get(`https://www.thecocktaildb.com/api/json/v1/1/lookup.php?i=${idDrink}`);
// console.log(res.data.drinks);
this.setState({cocktailrecipe: res.data.drinks.id, loading: false})
} catch(error) {
console.log(error)
}
}
handleClearCocktails= () => {
this.setState({cocktails:[], loading: false})
}
handleSetAlert=(msgfromSearch, typefromSearch)=>{
this.setState({ msg:msgfromSearch, type:typefromSearch })
setTimeout(()=>this.setState({msg:'', type:''}), 5000)
}
render() {
const {cocktails, loading, cocktailrecipe} = this.state;
return (
<Router>
<div className="App">
<Navbar title="COCKTAIL LIBRARY" />
<div className="container">
<Alert msg={this.state.msg} type={this.state.type} />
<Switch>
<Route exact path='/' render={props=>(
<Fragment>
<Search searchCocktails={this.handleSearchCocktails} clearCocktails={this.handleClearCocktails} showClear={this.state.cocktails.length>0?true:false} setAlert={this.handleSetAlert} />
<CocktailList loading={loading} cocktails={cocktails} />
</Fragment>
)} />
<Route exact path='/about' component={About} />
<Route exact path='/cocktailRecipe/:idDrink' render={props => (
<CocktailRecipe {...props} getCocktail={this.getCocktail} cocktailrecipe={cocktailrecipe} loading={loading}/>
)} />
</Switch>
</div>
</div>
</Router>
);
}
}
export default App;````
In your screenshot, props cocktailrecipe is an array of object.
Use array desctructuring instead of object on CocktailRecipe.js
- const {strDrink} = this.props.cocktailrecipe;
+ const [strDrink] = this.props.cocktailrecipe;
So turns out that my wifi connection is part of the problem. And grabbing the wrong object.
In CocktailRecipe.js I added in:
line 22:
const { drinks } = this.props.cocktailrecipe;
and then put into the render():
{drinks && drinks[0].strDrink }
I'm told that this may not be the most elegant or efficient solution, so if anybody has a better way, please let me know.
Related
*Sorry in advance I couldn't split the code into separate components in the post
I have a question that I will be able to explain well
I have an authentication component that receives a prop from another component
Now when I put all the components under one big parent component that contains all the components everything works fine and the props pass successfully
But as soon as I start using route I get this message
"Uncaught (in promise) TypeError: this.props.displaychange is not a function"
whet I tried to do here is to change the state in the TeamList component (state.displayTeams to true)
import './App.css';
import React from 'react'
import { Route, Routes } from 'react-router-dom'
import Home from './components/Home/Home';
import LogIn from './components/Log-in/Log-in';
import SignIN from './components/Sign-in/Sign-in';
import TeamList from './components/Team-list/Team-list';
import TeamDisplay from './components/TeamDisplay/TeamDisplay';
function App() {
return (
<div>
<Routes>
<Route path='/' exact element={<Home />} />
<Route path='/login' element={<LogIn />} />
<Route path='/sign-in' element={<SignIN />} />
<Route path='/teamlist' element={<TeamList />} />
<Route path='/teamdisplay' element={<TeamDisplay />} />
</Routes>
</div>
);
}
export default App;
import React, { Component } from "react";
import { signInWithEmailAndPassword, signOut } from 'firebase/auth'
import { auth } from '../../firebase-config'
import './Log-in.css'
import { Link } from "react-router-dom";
class LogIn extends Component {
state = {
logEmail: '',
logPass: '',
user: '',
login: false
}
login = async (displaychange, e) => {
e.preventDefault()
this.setState({ login: true })
const user = await signInWithEmailAndPassword(auth, this.state.logEmail, this.state.logPass)
console.log(user.user.email)
this.setState({ user: user.user.email })
this.setState({ logEmail: '', logPass: '' })
console.log('logged in')
this.props.displaychange()
}
logOut = async (displaychangeOut, e) => {
e.preventDefault()
if (this.state.login) {
await signOut(auth)
this.setState({ user: '', logEmail: '', logPass: '' })
console.log('you out')
this.props.displaychangeOut()
} else {
return
}
}
render() {
return (
<div className="Login-form">
<form>
<span>ACCOUNT LOGIN</span>
<label>USERNAME </label>
<input value={this.state.logEmail} onChange={(e) => this.setState({ logEmail: e.target.value })} name="nameLog" placeholder="Your name..." />
<label>PASSWORD </label>
<input value={this.state.logPass} onChange={(e) => this.setState({ logPass: e.target.value })} name="passLog" placeholder="Your password..." />
<button onClick={(e) => this.login(this.props.displaychange, e)}>LOG IN</button>
</form>
</div>
)
}
}
export default LogIn
import React, { Component } from "react";
import { db } from '../../firebase-config'
import { collection, addDoc, getDocs, getDoc, doc } from 'firebase/firestore'
import TeamDisplay from "../TeamDisplay/TeamDisplay";
import LogIn from "../Log-in/Log-in";
const teamCollectionRef = collection(db, 'Teams')
class TeamList extends Component {
state = {
teams: [
],
displayTeams: false
}
componentDidMount() {
getDocs(teamCollectionRef)
.then(snap => {
let teams = []
snap.docs.forEach(doc => {
teams.push({ ...doc.data() })
});
this.setState({ teams: teams });
});
}
changedisplay = () => {
this.setState({ displayTeams: true })
}
changedisplayOut = () => {
this.setState({ displayTeams: false })
}
render() {
let displayTeam
if (this.state.displayTeams) {
displayTeam = this.state.teams.map(item => <TeamDisplay key={item.teamId} team={item} />)
} else {
displayTeam = null
}
return (
<div>
{displayTeam}
</div>
)
}
}
export default TeamList
I am creating my first react project, i am using GitHub api to fetch user and display them firstly in card view then on clicking on more button to any profile i want to create a modal using portals in react till now i am able to create an modal but now i am not getting how to get data to that modal coponent
Here is my App.js
import React, { Fragment, Component } from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import Navbar from './components/layout/Navbar';
import Users from './components/users/Users';
import User from './components/users/User';
import Modal from './components/Modal/Modal'
import Search from './components/users/Search';
import Alert from './components/layout/Alert';
import About from './components/pages/About';
import axios from 'axios';
import './App.css';
class App extends Component {
state = {
users: [],
user: {},
loading: false,
alert: null,
modal: {},
}
// get users from Search.js
searchUsers = async text => {
this.setState({ loading: true })
const res = await axios.get(
`https://api.github.com/search/users?q=${text}&client_id=${
process.env.REACT_APP_GITHUB_CLIENT_ID
}&client_secret=${process.env.REACT_APP_GITHUB_CLIENT_SECRET}`);
this.setState({ users: res.data.items, loading: false })
console.log(text);
}
//get single profile
getUser = async username => {
this.setState({ loading: true })
const res = await axios.get(
`https://api.github.com/users/${username}?client_id=${
process.env.REACT_APP_GITHUB_CLIENT_ID
}&client_secret=${process.env.REACT_APP_GITHUB_CLIENT_SECRET}`);
this.setState({ user: res.data, loading: false });
this.setState({ modal: res.data, loadading: false });
}
//clear search
clearUsers = () => this.setState({ users: [], loading: false });
setAlert = (msg, type) => {
this.setState({ alert: { msg: msg, type: type } });
setTimeout(() => this.setState({ alert: null }), 5000);
};
render() {
return (
<Router>
<div className='App'>
<Navbar />
<div className="container">
<Alert alert={this.state.alert} />
<Switch>
<Route exact path='/'
render={props => (
<Fragment>
<Search
searchUsers={this.searchUsers}
clearUsers={this.clearUsers}
showClear={this.state.users.length > 0 ? true : false}
setAlert={this.setAlert}
/>
<Users loading={this.state.loading} users={this.state.users} />
</Fragment>
)} />
<Route path='/about' component={About} />
<Route path='/user/:login' render={props => (
<User {...props} getUser={this.getUser} user={this.state.user} loading={this.state.loading} />
)} />
<Route path='/modal/:login' render={props => (
<Modal {...props} getUser={this.getUser} modal={this.state.modal} loading={this.state.loading} />
)} />
</Switch>
</div>
</div>
</Router>
);
}
}
export default App;
here is my Modal.js
import React, { Fragment, Component } from 'react';
import ReactDom from 'react-dom';
import Spinner from '../layout/Spinner';
import { Link } from 'react-router-dom';
const modalRoot = document.getElementById('modal');
export default class Modal extends Component {
constructor() {
super();
this.el = document.createElement('div');
}
componentDidMount = () => {
modalRoot.appendChild(this.el);
};
componentWillUnmount = () => {
modalRoot.removeChild(this.el);
};
render() {
const {
children,
name,
avatar_url,
location,
bio,
blog,
followers,
following,
public_repos,
} = this.props.modal;
const { loading } = this.props;
if (loading) return <Spinner />
return (
ReactDom.createPortal(children, this.el)
)
}
}
any guide would be appriciated thanks in advance
You are passing the props already to Modal.
In Modal, do something like
Class Modal extends Component {
constructor(){
super(props);
}
render(){
const {
modal,
getUser,
loading,
anyOtherPropYouPassIn
} = this.props;
const { loading } = this.props;
if (loading) return <Spinner />
return (
ReactDom.createPortal(children, this.el)
)
}
I'm using React with the Wordpress REST API. The issue I am having is I cannot seem to wrap my head around how to (properly) use the component lifecycles to update the Post component when the slug property changes on the root App Component and fetching async data.
The way I have it set up currently, the App component state looks something like this:
this.state = {
pages: this.getPages(),
slug: this.getSlug(),
homePage: this.fetchPost('home'),
};
So the pages property is a promise and the App component initially renders a Spinner component. Eventually the async call receives a response. I perform a filter on the array of post objects to look for the current page post.
const thePage = this.state.pages.filter(page => {
return page.slug === slug;
});
The filter returns an array with one object (this current page). I update the state with this.setState({post: thePage[0]});
When I change change routes with react-router-dom, the slug and post are not updating. Below is my code.
index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import App from './components/App/App';
// Take the React component and show it on the screen
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root'));
App JS:
// App.js
import React, {Component} from 'react';
import { Route, withRouter } from 'react-router-dom';
import axios from 'axios';
import './App.scss';
import {wpAPI} from '../..//api';
import {/*loadState,*/ saveState} from '../loadState/loadState';
import FrontPage from '../../pages/FrontPage';
import Header from '../Header/Header';
import Post from '../Post/Post';
import Sidebar from '../Sidebar/Sidebar';
import Footer from '../Footer/Footer';
import Spinner from '../Spinner/Spinner';
// Create a React Component
class App extends Component {
constructor(props) {
super(props);
// Bindings
this.getPages = this.getPages.bind(this);
this.getPages();
this.state = {
isHome: false,
slug: this.props.location.pathname,
fetchingPages: true,
fetchingPost: true,
};
console.log('App State: (constructor)');
console.log(this.state);
}
/**
* Fetch Data
* #return {Promise}
*/
getPages = async () => {
const response = await axios.get(wpAPI['pages']);
this.setState({
fetchingPages: false,
pages: response.data
});
saveState(this.state);
}
getPage = (slug) => {
const thePage = this.state.pages.filter(page => {
return page.slug === slug.replace('/', '');
});
this.setState({
isHome: false,
fetchingPost: false,
post: thePage[0],
slug: slug,
});
}
/**
* The component has mounted. Fetch post based on slug
*/
componentDidMount() {
console.log('App State: (componentDidMount)');
console.log(this.state);
console.log('App Props: (componentDidMount)');
console.log(this.props);
}
componentDidUpdate(prevProps, prevState) {
console.log('App State: (componentDidUpdate)');
console.log(this.state);
const {fetchingPages, fetchingPost, isHome} = this.state;
const slug = this.props.location.pathname;
if (this.state.slug !== this.props.location.pathname) {
console.log('Slugs Dont match, getting page');
this.getPage(slug);
}
if (slug === '/' && !isHome) {
console.log('Setting isHome True');
this.setState({
isHome: true,
fetchingPost: false
});
}
if (fetchingPages === false && fetchingPost === true) {
console.log('Fetching Post');
this.getPage(slug);
}
}
renderContent() {
const {post, fetchingPost} = this.state;
if (!fetchingPost) {
return (
<section id="app" className="animated fadeIn">
<Header />
<main>
<Route path="/" exact render={(props) => <FrontPage {...props} /> } />
<Route path="/:slug" exact render={(props) => <Post post={post} /> } />
</main>
<Sidebar />
<Footer />
</section>
)
}
return <Spinner message='loading data...' />;
}
render() {
return this.renderContent();
}
};
export default withRouter(App);
Post.js
import React, {Component} from 'react';
import {withRouter} from 'react-router-dom';
import './Post.scss';
import Spinner from '../Spinner/Spinner';
class Post extends Component {
constructor(props) {
super(props);
this.state = {
slug: props.slug,
post: props.post,
};
}
componentDidMount() {
console.log('Post State: (componentDidUpdate)');
console.log(this.state);
}
componentDidUpdate() {
console.log('Post State: (componentDidUpdate)');
}
render() {
if ( this.state.post ) {
const {post} = this.state;
return (
<div className={`post post-${post.id} ${post.slug} animated fadeIn`}>
<header>
<h1 className="small-caps" dangerouslySetInnerHTML={{__html: post.title.rendered}}></h1>
</header>
<section id="content" dangerouslySetInnerHTML={{__html: post.content.rendered}}></section>
</div>
)
}
return <Spinner message='Fetching Post...'/>
}
}
export default withRouter(Post);
There's nothing in your code to say that when your App receives new props from the withRouter HOC, then update state.slug.
You could add:
this.setState({
slug: this.getSlug();
});
to your componentDidUpdate() function, however I'm not sure why you need it as state when it's available as a prop anyway in this.props.location.pathname which is being passed down to your FrontPage component, and could just as easily be passed down to your Posts component in the same way.
I'm attempting to read an array item in a child component via props. Logging the array in the child component works. But if I try to access a property of one of the array items by indexing it with the :id from match.params, it tells me that I can't access a property of 'undefined'.
Any guidance would be greatly appreciated.
tours.js
import React, { Component } from "react";
import { Route, Switch } from "react-router-dom";
// Page Imports
import Summary from "../pages/summary";
import Details from "../pages/details";
// Component Imports
import Homebutton from "../components/homebutton";
class Tours extends Component {
state = {
tours: []
};
componentDidMount() {
window.scrollTo(0, 0);
fetch("/tours")
.then(res => res.json())
.then(res => this.setState({ tours: res }));
}
render() {
const tours = this.state.tours;
return (
<section className="tours-page">
<div className="center-box">
<h2>Tours</h2>
</div>
<Switch>
<Route
exact
path={this.props.match.url}
render={props => <Summary {...props} tours={tours} />}
/>
<Route
path={this.props.match.url + "/:id"}
render={props => <Details {...props} tours={tours} />}
/>
</Switch>
<Homebutton />
</section>
);
}
}
export default Tours;
details.js
import React from "react";
const Details = ({
tours,
match: {
params: { id }
}
}) => (
<section className="details">
<h2>{tours[id]["name"]}</h2>
</section>
);
export default Details;
To be sure that tours[id] is not undefined you should check it first
<section className="details">
<h2>{tours[id] && tours[id]["name"]}</h2>
</section>
As componentDidMountalways gets called after first render, you must validate your props to avoid app crashes:
const Details = ({
tours,
match: {
params: { id }
}
}) => (
<section className="details">
<h2>{tours.length && tours[id]["name"]}</h2>
</section>
);
For some reason, my routes only render half the time - seems like a race condition of some sort. It'll print out the "OK" but nothing from the routes, not even the 404. Pretty clear cut.
If I remove the loading bit it'll always render the switch block as intended.
Is there a better / different way to do this?
v4.2.0
render() {
const { hasInitialized } = this.props;
if (!hasInitialized) {
return (
<div>Loading...</div>
);
}
return (
<div style={{ height: '100%', width: '100%' }}>
<Helmet titleTemplate="%s - Resilient" defaultTitle="Resilient" />
<div>OK</div>
<Switch>
<Redirect from="/" to="/auth/check" exact={true} />
<Route path="/auth" component={AuthLayout} />
<AuthenticatedRoute path="/x" component={AdminLayout} />
<Route component={Miss404} />
</Switch>
</div>
);
}
https://github.com/ReactTraining/react-router/issues/5621
I read the react-router docs many times, and the part about Blocked Updates seems relevant. But, when I put a debugger line in <Layout />, location and history always have the right info, and still, none of the routes would render.
I still don't understand what the issue was, but here's the workaround I came up with. The code below wraps my <Layout /> component, which contains all the routes.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import LocalStorageManager from 'utils/LocalStorageManager';
import { selectCurrentUser, selectHasInitialized } from 'client/selectors/authSelectors';
import { setAccessToken, getProfile } from 'shared/api';
import { setHasInitialized, signIn } from 'modules/auth/actions.js';
import SinglePageCard from 'components/layout/SinglePageCard';
const mapStateToProps = (state) => {
return {
currentUser: selectCurrentUser(state),
hasInitialized: selectHasInitialized(state),
};
};
export default (WrappedComponent) => {
class Layout extends Component {
componentWillMount() {
const accessToken = LocalStorageManager.getAccessToken();
if (!accessToken) {
this.props.setHasInitialized();
return;
}
setAccessToken(accessToken);
getProfile().then((response) => {
console.log(response);
const { user } = response.data.data;
this.props.signIn(user);
}).catch((error) => {
console.log(error);
this.props.setHasInitialized();
});
}
render() {
const { currentUser, hasInitialized, ...rest } = this.props;
if (!hasInitialized) {
return (
<SinglePageCard>
<div>Initializing...</div>
</SinglePageCard>
);
}
return (
<WrappedComponent {...rest} />
);
}
}
return withRouter(connect(mapStateToProps, { setHasInitialized, signIn })(Layout));
};