I am trying to call api data only once thats way I call api in home.js file with componentdidmount in class component and i want to render this data in many child components with functional components.when i call api in every each child component,its work but when i try to call with props coming only empty array by console.log please help.
import React from 'react'
import '../styles/home.css'
import axios from 'axios';
import Teaser from './Teaser'
import Second from './Second'
import Opening from './Opening'
import Menu from './Menu'
export default class Home extends React.Component {
state = {
posts: []
}
componentDidMount() {
axios.get("https://graph.instagram.com/me/media?fields=id,caption,media_url,permalink,username&access_token=IGQ")
.then(res => {
const posts = res.data.data;
this.setState({ posts });
})
}
render() {
return (
<>
<Teaser/>
<Second/>
<Opening/>
<Menu posts={this.state.posts}/>
</>
)
}
}
import React from 'react'
import axios from 'axios';
function Menu(props) {
const {posts} = props.posts;
console.log(props);
return (
<>
{posts.map(
(post) =>
post.caption.includes('#apegustosa_menu') &&
post.children.data.map((x) => (
<div className="menu_item" key={x.id}>
<img className="menu_img" src={x.media_url} alt="image" />
</div>
)),
)}
</>
)
}
export default Menu
Related
I am trying to fetch data from a backend API and initialise my FieldsContext. I am unable to do it, it returns an empty fields array in the Subfields component. I have spent hours on fixing it. But I eventually give up. Please take a look into this. Thanks in advance.
Here is my code
App.js
import React from 'react';
import 'bootstrap/dist/css/bootstrap.min.css'
import './App.css';
import Index from './components/pages/index/'
import FieldsProvider from './providers/fieldProvider'
import AuthProvider from './providers/authProvider'
import {BrowserRouter as Router,Switch,Route} from 'react-router-dom';
import SubFields from './components/pages/subfields';
function App() {
return (
<Router>
<AuthProvider>
<FieldsProvider>
<Switch>
<Route exact path="/" component={Index} />
<Route exact path="/:fieldid/subfields" component={SubFields} />
</Switch>
</FieldsProvider>
</AuthProvider>
</Router>
);
}
export default App;
FieldsContext.js
import React from 'react'
const FieldsContext = React.createContext();
export default FieldsContext
FieldsProvider.js
import React, { Component } from 'react'
import FieldsContext from '../libs/fieldContext'
export default class FieldsProvider extends Component {
state = {fields:[]}
getFields()
{
fetch('/api/fields')
.then(res => res.json())
.then(fields => this.setState({fields}));
}
async componentDidMount() {
await this.getFields();
}
render() {
return (
<FieldsContext.Provider value={this.state} >
{this.props.children}
</FieldsContext.Provider>
)
}
}
Subfields.js
import React, { Component } from 'react'
import FieldsContext from '../../../libs/fieldContext'
import FieldsList from '../../Fields/fieldlist'
export default class SubFields extends Component {
componentDidMount(){
// const fieldId = this.props.match.params.fieldid;
console.log(this.context);
}
render() {
return (
<div>
</div>
)
}
}
SubFields.contextType = FieldsContext
try using an ES6 Arrow function, which binds the function to the object instance, so that this refers to the object instance of the class when it is called.
When its called asynchronously, this will refer the the class object instance you want to update.
import React, { Component } from 'react'
import FieldsContext from '../libs/fieldContext'
export default class FieldsProvider extends Component {
state = {fields:[]}
// ES6 Arrow function
getFields = () =>
{
fetch('/api/fields')
.then(res => res.json())
.then(fields => this.setState({fields}));
}
async componentDidMount() {
await this.getFields();
}
render() {
return (
<FieldsContext.Provider value={this.state} >
{this.props.children}
</FieldsContext.Provider>
)
}
}
Alternatively, Try binding of your function in the class constructor.
export default class FieldsProvider extends Component {
state = {fields:[]}
constructor(props) {
//bind the class function to this instance
this.getFields = this.getFields.bind(this);
}
//Class function
getFields()
{
fetch('/api/fields')
.then(res => res.json())
.then(fields => this.setState({fields}));
}
async componentDidMount() {
await this.getFields();
}
render() {
return (
<FieldsContext.Provider value={this.state} >
{this.props.children}
</FieldsContext.Provider>
)
}
}
As a side note: Prefer to use functional components for consuming of ContextAPI.
import React, { Component } from 'react'
import FieldsContext from '../../../libs/fieldContext'
import FieldsList from '../../Fields/fieldlist'
export default function SubFields (props) {
const {
match
} = props;
//much better way to consume mulitple Contexts
const { fields } = React.useContext(FieldsContext);
//useEffect with fields dependency
React.useEffect(() => {
console.log(fields);
},[fields]);
return (
<div>
</div>
)
}
I'm using NextJs in a project and and I created a component where I load dynamic data, if I load via localhost:3000/faq, it works normally, but if I try to import that same component into index.js, an error occurs. I probably need to pass props, but I don't know how to do that.
This is my faq.js
import React from 'react'
import fetch from 'isomorphic-unfetch'
function Faq({ data }) {
return (
<div>
<ul>
{data.map((item) => (
<li key={item.id}>{item.question}{item.answer}</li>
))}
</ul>
</div>
)
}
export async function getStaticProps() {
const res = await fetch('./data/faq.json')
const data = await res.json()
return {
props: {
data,
},
}
}
export default Faq
Here is the index.js
import Layout from '../components/layouts/layout'
import Faq from './faq'
import React, {Component} from 'react'
export default class App extends Component {
render() {
return(
<Layout>
<h1>I am Home Page</h1>
<Faq />
</Layout>
)
}
}
Does anyone know how to load faq.js into index.js?
Inside index.js you've import ./faq with name About but inside the render function you used it as <Faq />. Should be in this way:
import Layout from '../components/layouts/layout'
import Faq from './faq'
import React, {Component} from 'react'
export default class App extends Component {
render() {
return(
<Layout>
<h1>I am Home Page</h1>
<Faq />
</Layout>
)
}
}
I have recently started building a big project on React using also a Firebase with authentication and I cannot quite understand the relation between the react-router-dom links and React components.
I am struggling with getting the
this.props.match.params // which is going to be 2018 / 2019 / 2020... etc
in the component, which renders as a dynamic route (like unique post component).
I have tried to use only a simple class component and this works but the problem is, without the authentication everyone can access this admin route and everyone would be allowed to edit and delete data there. I want it to be accessed only by authenticated users. (Admins)
So this is how my piece of code looks like:
Main component: (where the link is)
import React, { Component } from 'react'
import { Link } from 'react-router-dom'
class SeasonBox extends Component {
render() {
return (
<Link className='seasonbox' to={`/adminseason/${this.props.season}`}>
<p className='seasonbox__season'>{this.props.season}/{this.props.season+1}</p>
</Link>
)
}
}
export default SeasonBox;
And the component that renders after the link is clicked:
import React, { Component } from 'react'
import { Link } from 'react-router-dom'
import { connect } from 'react-redux'
import { compose } from 'recompose'
import { withAuthorisation } from '../Session'
import { withFirebase } from '../Firebase'
const AdminMatchesBox = ({authUser}) => (
<div>{authUser ? <AdminMatchesBoxAuth /> : <AdminMatchesBoxNonAuth />} </div>
)
class AdminMatchesBoxAuth extends Component {
render() {
return (
<div>
Hey I am the season {this.props.match.params}!
<Link to={'/adminmatches'}>Wróć</Link>
</div>
)
}
}
const AdminMatchesBoxNonAuth = () => (
<div>
<h1>You do not have permission to visit this page.</h1>
</div>
)
const mapStateToProps = state => ({
authUser: state.sessionState.authUser
});
const condition = authUser => !!authUser
export default compose(withAuthorisation(condition), connect(mapStateToProps),withFirebase)(AdminMatchesBox);
So if I don't use authorisation, and I use only a single class component I can get this.props.match.params -> which is the id of the website and I need it to access data from the database.
However, I want it to not be visible by not logged users and I had to process it through the authorisation process.
I am receiving an error
Cannot read property 'params' of undefined.
I have no clue how to pass match.params into the AdminMatchesBoxAuth component.
Could anyone advice?
By wrapping withRouter you able to access params
Try this
import { withRouter } from "react-router";
import React, { Component } from 'react'
import { Link } from 'react-router-dom'
import { connect } from 'react-redux'
import { compose } from 'recompose'
import { withAuthorisation } from '../Session'
import { withFirebase } from '../Firebase'
const AdminMatchesBox = ({authUser}) => (
<div>{authUser ? <AdminMatchesBoxAuth /> : <AdminMatchesBoxNonAuth />} </div>
)
class AdminMatchesBoxAuth extends Component {
constructor (props){
super(props)
}
render() {
return (
<div>
Hey I am the season {this.props.match.params}!
<Link to={'/adminmatches'}>Wróć</Link>
</div>
)
}
}
const AdminMatchesBoxNonAuth = () => (
<div>
<h1>You do not have permission to visit this page.</h1>
</div>
)
const mapStateToProps = state => ({
authUser: state.sessionState.authUser
});
const condition = authUser => !!authUser
export default compose(withRouter, withAuthorisation(condition), connect(mapStateToProps),withFirebase)(AdminMatchesBox)
Ok so in the render method I pass the gifs state to my GifList component the problem is when I try to use that array in that component thru props its saying that its undefined and upon further review I can see that the gifs property in the app's state is originally being passed as an empty array before the setState is setting it to the return value of my Axios call in the lifecycle hook because of Axios being async. How can I fix this issue??
import React, { Component } from 'react';
import axios from "axios";
import styles from './App.css';
import Header from './Components/Header/Header';
import GifList from './Components/GifList/GifList';
class App extends Component {
state = {
title: "Giphy Search App",
gifs: []
}
componentDidMount() {
axios.get("http://api.giphy.com/v1/gifs/search? q=funny+cat&limit=20&api_key=ms344CewNH5NEbybHwQifMZImoQfEQ38")
.then((res) => {
const arr = res.data.data;
this.setState({ gifs: arr });
});
}
render() {
return (
<div className={styles.app}>
<Header title={this.state.title}/>
<GifList gifList={this.state.gifs}/>
</div>
);
}
}
export default App;
You can wait to render your GifList until your gifs array has something in them. This is basically an inline if statement for jsx.
render() {
return (
<div className={styles.app}>
<Header title={this.state.title}/>
{this.state.gifs.length > 0 && <GifList gifList={this.state.gifs}/>}
</div>
);
}
you can render GifList only after the list has some items
render() {
return (
<div className={styles.app}>
<Header title={this.state.title}/>
{
this.state.gifs.length &&
<GifList gifList={this.state.gifs}/>
}
</div>
);
}
export default App;
I successfully load data from the OpenDota API but somehow, the images
are broken when I pass the image props in my Heroes.js
here is the Component where I load the API.
HeroStats.js
import React, { Component } from 'react'
import Sidebar from "./Sidebar";
import Heroes from "./Heroes"
import "./App.css"
import axios from "axios";
const URL = "https://api.opendota.com/api/heroStats";
class HeroStats extends Component {
state = {
data: []
}
componentDidMount() {
axios.get(URL)
.then(res => {
this.setState({
data: res.data
});
});
}
render() {
const Stats = this.state.data.map(stat => (
<Heroes
key={stat.id}
id={stat.id}
name={stat.name}
localized_name={stat.localized_name}
img={stat.img}
icon={stat.icon}
pro_win={stat.pro_win}
pro_pick={stat.pro_pick}
pro_ban={stat.pro_ban}
/>
))
return (
<div>
{Stats}
</div>
)
}
}
export default HeroStats;
and here where I pass my props.
Heroes.js
import React from 'react'
const Heroes = (props) => (
<div>
<h1>{props.localized_name}</h1>
<img src={props.img} />
<img src={props.icon} />
<h1>{props.pro_win}</h1>
<h1>{props.pro_pick}</h1>
<h1>{props.pro_ban}</h1>
</div>
)
export default Heroes;
also if I use other tag like <h1>{props.img}</h1> it shows the image file path. did I miss something that i should include?
the value of img is not the full url you need to do this
const Heroes = (props) => (
<div>
<h1>{props.localized_name}</h1>
<img src={"https://api.opendota.com" + props.img} />
<img src={"https://api.opendota.com" + props.icon} />
<h1>{props.pro_win}</h1>
<h1>{props.pro_pick}</h1>
<h1>{props.pro_ban}</h1>
</div>
)