Using one Axios object for the whole application using Context - reactjs

I have an application with several levels of children. In this example I have declared a Context.js and a ContextDBConnector.js , they are being used in this way:
Context.js
import React from 'react';
export const ContextDB = React.createContext();
ContextDBConnector.js
import React, {Component} from 'react'
import {ContextDB} from './Context'
class ContextDBConnector extends Component {
render(){
return (
<ContextDB.Provider value = {{
callAPI() {
const axios = require('axios').default
axios.get('http://localhost:5000/person_by_first_name', {
params: {
firstname: "Marcus"
}
})
.then(function (response) {
console.log("Printing out response")
console.log(response);
})
.catch(function (error) {
console.log(error);
})
.then(function () {
// always executed
})
}
}}>
{this.props.children}
</ContextDB.Provider>
)
}
}
export default ContextDBConnector;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import * as serviceWorker from './serviceWorker';
import App from './components/App.js';
import {navData} from './js/const';
import {footerData} from './js/const';
import ContextDBConnector from './context/ContextDBConnector'
ReactDOM.render(
<React.StrictMode>
<ContextDBConnector>
<App key = "0" navData = {navData} footData = {footerData} />
</ContextDBConnector>
</React.StrictMode>,
document.getElementById('root')
);
serviceWorker.unregister();
So far so good. I can now use the context to call context.callAPI() from inside a component far away down the line of children, in this way:
Image.js
import React, {Component} from 'react'
import {ContextDB} from '../context/Context'
class Image extends Component {
static contextType = ContextDB
componentDidMount(){
this.context.callAPI()
}
render(){
const obj = this.props.obj
return (
<div className={obj.classCont}>
<img className={obj.class} src={obj.url} alt='' />
</div>
);
}
}
export default Image;
This works well and looking at the javascript console in Chrome, ContextDBConnector.js is printing out what it should:
Printing out response
ContextDBConnector.js:23 {data: Array(1), status: 200, statusText: "OK", headers: {…}, config: {…}, …}
Now I'm having a hard time getting the response object from component ContextDbConnector to component Image so that it can use the data it needs. I tried something like this and it obviously didn't work:
Image.js
import React, {Component} from 'react'
import {ContextDB} from '../context/Context'
class Image extends Component {
static contextType = ContextDB
componentDidMount(){
this.context.callAPI().then(response => {
console.log("Person has been returned")
});
}
render(){
const obj = this.props.obj
return (
<div className={obj.classCont}>
<img className={obj.class} src={obj.url} alt='' />
</div>
);
}
}
export default Image;
Could you help me figure out the part where Image gets the data it needs from ContextDBConnector ?
Full disclosure: I only started learning React, Express, Mysql, Axios and Node.js like a month ago, so I apologize in advance if I'm misunderstanding some concepts here and my code logic is not very strong :D This is my first try with Context and I'm a bit stuck with this part.
Just so it's clear, what I'm trying to accomplish here is to have only one axios object for the whole app, so that the different parts can access it and get to the different routers in the Express server (backend) instead of creating axios instances all over the place.
If the way I'm trying to accomplish this is not a good one, could you show me a better way?
Otherwise, could you help me get the data from ContextDBConnector to Image in a way that Image waits for the actual object to be received before doing anything else?
Thanks and much appreciated!

as it seems you should have changed the callAPI 'cause it does not return a Promise Object so you could use .then() and .catch()

Related

My ReactJS module page component is not rendering for some reason

On my react application, the module page component is not rendering, this is the error is receive when I load the component. In this component, I am trying to read data from the firebase real time database and then display it on the page.
Uncaught ReferenceError: initialize is not defined
I also get this error as well
Uncaught Error: ModulePage(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.
The following is my code for the module page component
import Header from './header.js';
import Footer from './footer.js'
import './review_index.css';
import { Card, Button } from "react-bootstrap";
import React, {useEffect, initialize} from 'react';
import { useNavigate} from "react-router-dom";
import {realtimeDB, auth } from '../firebase-config';
import { ref, onValue } from 'firebase/database';
import { useAuthState } from 'react-firebase-hooks/auth';
function ModulePage(){
let navigate = useNavigate();
//if statement that checks if the page is being accessed by the user
const [user] = useAuthState(auth);
if(!user){
navigate("/");
}
//function to get the data
const getData = () => {
const data = ref(realtimeDB, 'Modules')
onValue(data, (snapshot) => {
return(
<>
<Header />
<h1>Welcome to the module page</h1>
<p> on this page, yopu will see the modules that you can currently upload reviews on</p>
<Card className="module_card">
<h2>Module list</h2>
<p>{snapshot.val()}</p>
<Button className="moduleselection" onClick={navigate("/ModuleReviewForm")}> </Button>
</Card>
<div className="moduleselection"></div>
<Footer />
</>
);
})
}
initialize = true
useEffect(() => {
getData();
}, [initialize])
}
export default ModulePage;
I really need help on this, any advice will be much appreciated.
You have this import:
import React, {useEffect, initialize} from 'react';
And initialize does not exist in the React library, remove this one import and declare initialize as a variable on your code.

ReactJS: Problem accessing this.context in a class based consumer component

I have a problem to access this.context in a class based consumer component. I have the following situation:
AppContext.js:
import React from "react";
const ContactContext = React.createContext(); // Create our context
export default ContactContext;
DataProvider.js:
import React, { Fragment } from "react";
import AppContext from "./AppContext";
export default class DataProvider extends React.Component {
state = {
contacts: {
contact1: {
id: 1,
firstName: 'Test User FN',
lastName: 'Test User LN'
}
}
};
render() {
return (
<>
<AppContext.Provider value={{contacts: this.state.contacts}}>
{this.props.children}
</AppContext.Provider>
</>
);
}
}
App.js:
import React from 'react';
import DataProvider from "./DataProvider";
import Contact from './components/contact/contact.component';
export default class App extends React.Component {
render() {
return (
<div>
<div className="container">
<DataProvider>
<Contact contactIndex={0}/>
</DataProvider>
</div>
</div>
);
}
}
The consumer Contact.js:
import React, { Component } from "react";
import AppContext from '../context/AppContext'
export default class Contact extends Component {
static contextType = AppContext;
componentDidMount () {
console.log('My context is: ' + this.context);
}
render() {
return (
<div className="card"></div>
);
}
}
The console output is:
My context is: undefined
Thanks for any help
Regards
Dakir
Only difference I see in the other answer's CodeSandbox is the import path is different.
import AppContext from "./AppContext";
vs:
import AppContext from '../context/AppContext'
Maybe OP has a filepath/import error?
p.s. If this is the error, TypeScript is a lifesaver for avoiding these kind of things in JS.
Your code seems right to me, I tried to replicate it in a Sandbox to find out the error and somehow works like a charm.
https://codesandbox.io/s/interesting-https-emgoz?file=/src/App.js
Tried to spot the difference but I couldn't honestly.

Why the data not displayed in nextjs?

I am making a very very simple nextjs application where I am trying to fetch the data from api.
My requirement is I should display the data in layout.js file and this layout.js file is a children in index.js file.
index.js:
import Layout from "./layout";
import React from "react";
class Home extends React.Component {
render() {
return (
<div>
<Layout />
<h4> Main content will be displayed here !! </h4>
</div>
);
}
}
export default Home;
layout.js:
import React from "react";
import fetch from "isomorphic-unfetch";
function Layout(props) {
return (
<div>
<p>Preact has {props.stars} ⭐</p>
<p> Why I couldn't get the above "props.star" ? </p>
</div>
);
}
Layout.getInitialProps = async () => {
console.log("comes into layout getinitial props");
const res = await fetch("https://api.github.com/repos/developit/preact");
const json = await res.json(); // better use it inside try .. catch
return { stars: json.stargazers_count };
};
export default Layout;
So as per the above given code, I have called the layout page inside index.js page (in my real application I need to call like this only so no changes in calling layout inside index)..
But when I made a console.log() in the function Layout.getInitialProps in layout, it doesn't print anything and hence the api data not fetched..
Complete working demo here with code
Why can't I fetch the data inside the layout.js while calling as a children from index.js?
Also provide me the right updated solution to achieve this.. I really searched for many questions but none solved my issue and I couldn't understand those solutions clearly so please help me with the above given example.
That because getInitialProps can only be added to the default component exported by a page, adding it to any other component won't work.
You should use componentDidMount() or useEffect instead, or move getInitialProps in the index and then pass the result to the component. something like (not tested) :
index.js :
import Layout from "./layout";
import React from "react";
class Home extends React.Component {
render() {
return (
<div>
<Layout />
<h4> Main content will be displayed here !! </h4>
</div>
);
}
}
export default Home;
layout.js
import React from "react";
import fetch from "isomorphic-unfetch";
class Layout extends React.Component {
constructor(props) {
super(props);
this.state = {
stars: false
};
}
async componentDidMount() {
console.log("comes into layout getinitial props");
const res = await fetch("https://api.github.com/repos/developit/preact");
const json = await res.json(); // better use it inside try .. catch
this.setState({ stars: json.stargazers_count });
}
render() {
const { stars } = this.state;
return (
<div>
<p>Preact has {stars} ⭐</p>
<p> Why I couldn't get the above "props.star" ? </p>
</div>
);
}
}
export default Layout;
Edit:
Example with class component
Bonus: If you want to add the layout for all the pages of your app this isn't the best approach, instead you should take a look to custom _app.js, example

How to test components using Mobx stores with Jest

I'm trying to test my React components using Mobx stores with Jest and React-testing-library.
The problem is that I have no clues on how to inject my stores for the test.
Here is my simplified codes.
StaffInfo.js(component)
import React, { useState } from "react";
import { observer, inject } from "mobx-react";
const StaffInfo = props => {
const store = props.instituteStore;
const [staffs, setStaffs] = useState(store.staffs);
return (
<div>
....
</div>
);
}
export default inject(rootStore => ({
instituteStore : rootStore.instituteStore
}))(observer(StaffInfo));
index.js(Root store)
import LoginStore from "./LoginStore";
import InstituteStore from "./InstituteStore";
class RootStore {
constructor(){
this.loginStore = new LoginStore (this);
this.instituteStore = new InstituteStore(this);
}
}
export default RootStore;
InstituteStore.js(target store)
import { observable, action } from "mobx";
class InstituteStore {
constructor(root){
this.root = root;
}
#observable
staffs = [];
}
export default InstituteStore;
StaffInfo.test.js(test file)
import React from "react";
import ReactDom from "react-dom";
import { MemoryRouter } from "react-router-dom";
import { Provider } from "mobx-react";
import StaffInfo from "./StaffInfo";
import InstituteStore from "../stores/InstituteStore";
describe("Staff Component testing", () => {
test("should be rendered without crashing", () => {
const div = document.createElement("div");
ReactDOM.render(
<MemoryRouter initialEntries={["/staff"]}>
<StaffInfo instituteStore={RootStore.instituteStore} />
</MemoryRouter>,
div
);
ReactDOM.unmountComponentAtNode(div);
});
});
As soon as running this test file, the error messages are like :
TypeError : Cannot read property 'staffs' of undefined
Please tell me which parts of the codes are wrong.
Thanks so much in advance!
Mobx-react's Inject is used to insert stores to the deep child component. These stars are provided by the context-based API Provider.
so wherever you are providing the stores to the child components use something like.
import rootStore from 'path_to_rootStore'
<Provider rootStore={rootStore}>
...
...
<App/>
...
...
<.Provider>
Thanks to #uneet7:
Legend! Finally someone gave a sensible answer :D
This is what My component looks like and
#inject('routing', 'navigationStore')
#observer
export default class PageTitle extends React.Component<*> {...}
And this is how I made it work:
let view = mount(
<Provider {...getStores()}>
<UserPage notificationStore={notificationStore} routing={routing} />
</Provider>
);
So the UserPage has components (many) and one of those components has PageTitle component. Obviously PageTitle has the #inject on it. It doesn't matter, as Provider HOC will provide stores via inject function to the component props.

React not passing props to children?

I'm trying to pass the data from this axios call into a child component, Hero. Despite having passed down the props and made a successful axios call it won't actually make it into the Hero div.
When I console.log on the child component it claims to have the data but then fails to push it to the champions array so I can't use it. Any ideas?
Edit:
I'll add in here that I do have react-router installed in this project however this data is being passed around across one "view" and not multiple pages.
This is the parent component
import React, { Component } from 'react';
import axios from 'axios';
import './assets/stylesheets/screen.css';
import Hero from './Hero';
import Info from './Info';
class Home extends Component {
constructor(props){
super(props);
this.state = { champions: [] };
}
componentDidMount() {
axios.get(
'https://api.pinterest.com/v1/boards/gasulliv/pose-
references/pins/?access_token=AQjW6hDdAF0egwEesZA6oJbqP0XQFQ-
m6_jg2RpErKPqdSA7cQAAAAA&limit=100&fields=id%2Clink%2Cnote%2
Curl%2Coriginal_link%2Cimage').then(champions => {
this.setState({ champions });
console.log(champions);
});
}
render() {
return (
<div>
<Hero champions = {this.state.champions} />
<Info />
</div>
);
}
}
export default Home;
And this is child component (at this console log I get two answers, one claiming it has the data and another claiming it does not):
import React from 'react';
import Header from './Header';
import 'bootstrap/dist/css/bootstrap.min.css';
import './assets/stylesheets/screen.css';
const Hero = (props) => {
console.log(props);
return (
<div className = "jumbotron kindred">
<Header />
<div className = "textHolder">{ props.champions.length }</div>
</div>
)
}
export default Hero;
You have to access the data in the data key response.data
Try the following.
axios.get('https://api.pinterest.com/v1/boards/gasulliv/pose-references/pins/?access_token=AQjW6hDdAF0egwEesZA6oJbqP0XQFQ-m6_jg2RpErKPqdSA7cQAAAAA&limit=100&fields=id%2Clink%2Cnote%2Curl%2Coriginal_link%2Cimage')
.then((response) => {
this.setState({
champions: response.data
})
})
.catch((error) => {
// Do something with the error
})
Thanks for help but it turns out the issue had to do with the fact that I had the router installed. Likely I just need to pass that data around through the router instead of the pages.
Kudos for the help!

Resources