Accessing data from service in other components - javascript

I was given below task in an interview, here the task is about getting a response from API using ajax call on button click and display it on a page.
I have a top component inside App.js, with two child components as MyButton.js and MyPage.js and the service code in MyAPI.js
Below are the file contents:
App.js
import React, { Component } from 'react';
import MyAPI from './services/MyAPI';
import MyButton from './components/MyButton';
import MyPage from './components/MyPage';
class App extends Component {
constructor() {
super();
this.state= {
'apiResponse': ''
};
}
handleButtonClick = () => {
MyAPI.getAPIResponse().then((res) => {
res => this.setState({ apiResponse })
});
}
render() {
return (
<div>
<center><MyButton onClickButton={this.handleButtonClick}></MyButton></center>
<MyPage apiResponse={this.props.apiResponse}></MyPage>
</div>
);
}
}
export default App;
MyButton.js
import React from 'react';
import PropTypes from 'prop-types';
import Button from '#material-ui/core/Button';
const MyButton = (() => (
<div className="button-container">
<MyButton variant="extendedFab" color="primary"
onClick={this.props.onClickButton}>
Call API
</MyButton>
</div>
));
MyButton.propTypes = {
onClickButton: PropTypes.func
}
export default MyButton;
MyPage.js
import React from 'react';
import PropTypes from 'prop-types';
import List from '#material-ui/core/List';
import ListItem from '#material-ui/core/ListItem';
import ListItemText from '#material-ui/core/ListItemText';
import Paper from '#material-ui/core/Paper';
const MyPage = (() => (
<Paper className="container">
<List>
<ListItem>
<ListItemText>Name: {this.props.apiResponse.split(" ")[0]}</ListItemText>
</ListItem>
</List>
</Paper>
));
MyPage.propTypes = {
apiResponse: PropTypes.string
}
export default MyPage;
MyAPI.js
import axios from 'axios';
export default {
getAPIResponse() {
return axios.get("--url to get user name and age as json--").then(response => {
return response.data;
});
}
};
Here the JSON data contains the name of a sample user just for demo purpose eg: John Doe. I need to display only John on my page as per the given task.
When I run this application I am getting errors at my MyButton.js and MyPage.js in logs.
In MyButton.js the error is at line onClick={this.props.onClickButton}, it says cannot access props on undefined. If I change it to onClick={this.onClickButton}, I got an error as, cannot access onClickButton on undefined. What is the correct way to do this here, please help.
Also same applies to MyPage.js at line {this.props.apiResponse.split(" ")[0], also is it the right way to use the split method here to get the first name from John Doe?

Your MyButtn and MyPage both are functional components. To access the props you do not need to use this. props are taken as params in case of functional components.
MyButton
const MyButton = ((props) => (
<div className="button-container">
<MyButton variant="extendedFab" color="primary"
onClick={props.onClickButton}>
Call API
</MyButton>
</div>
));
MyPage
const MyPage = ((props) => (
<Paper className="container">
<List>
<ListItem>
<ListItemText>Name: {props.apiResponse.split(" ")[0]}</ListItemText>
</ListItem>
</List>
</Paper>
));

once the response success you have to store in the variable
var a = "jhon doe";
var data = a.split(" ");
data[0];
you can do this in a parent component.

Related

ReactJs: How to get api data in child component with props?

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

How to use react-router-dom v6 navigate in class component

I installed react-router-dom v6 and I want to use a class based component, in previous version of react-router-dom v5 this.props.history() worked for redirect page after doing something but this code not working for v6 .
In react-router-dom v6 there is a hook useNavigate for functional component but I need to use it in class base component , Please help me how to use navigate in class component ?
In the react-router-dom v6, the support for history has been deprecated but instead of it, navigate has been introduced. If you want to redirect user to a specific page on success of a specific event, then follow the steps given below:
Create a file named as withRouter.js, and paste the code given below in this file:
import { useNavigate } from 'react-router-dom';
export const withRouter = (Component) => {
const Wrapper = (props) => {
const navigate = useNavigate();
return (
<Component
navigate={navigate}
{...props}
/>
);
};
return Wrapper;
};
Now, in whichever class based component you want to redirect the user to a specific path/component, import the above withRouter.js file there and use this.props.navigate('/your_path_here') function for the redirection.
For your help, a sample code showing the same has been given below:
import React from 'react';
import {withRouter} from '.your_Path_To_Withrouter_Here/withRouter';
class Your_Component_Name_Here extends React.Component{
constructor(){
super()
this.yourFunctionHere=this.yourFunctionHere.bind(this);
}
yourFunctionHere()
{
this.props.navigate('/your_path_here')
}
render()
{
return(
<div>
Your Component Code Here
</div>
)
}
}
export default withRouter(Your_Component_Name_Here);
Above Code works Perfect. And this is just a small extension.
If you want onclick function here is the code:
<div className = "row">
<button className= "btn btn-primary"
onClick={this.yourFunctionHere}>RedirectTo</button>
</div>
in class base component for redirect user follow this step :
first import some component like this
import { Navigate } from "react-router-dom"
now make a state for Return a boolean value like this:
state = {
redirect:false
}
now insert Naviagate component to bottom of your component tree
but use && for conditional rendring like this :
{
this.state.redirect && <Navigate to='/some_route' replace={true}/>
}
now when you want redirect user to some page just make true redirect state
on a line of code you want
now you can see you navigate to some page :)
Try this:
import {
useLocation,
useNavigate,
useParams
} from "react-router-dom";
export const withRouter = (Component) => {
function ComponentWithRouterProp(props) {
let location = useLocation();
let navigate = useNavigate();
let params = useParams();
return (
<Component
{...props}
router={{ location, navigate, params }}
/>
);
}
return ComponentWithRouterProp;
}
and just used this function, in my case:
import { withRouter } from '../utils/with-router';
import './menu-item.styles.scss';
const MenuItem = ({title, imageUrl, size, linkUrl,router}) =>(
<div
className={`${size} menu-item`} onClick={() => router.navigate(`${router.location.pathname}${linkUrl}`)}
>
<div className='background-image'
style={{
backgroundImage: `url(${imageUrl})`
}} />
<div className="content">
<h1 className="title">{title.toUpperCase()}</h1>
<span className="subtitle">SHOP NOW</span>
</div>
</div>
)
export default withRouter(MenuItem);
I found this solution here https://www.reactfix.com/2022/02/fixed-how-can-i-use-withrouter-in-react.html
Other solution is useNavigate, for example:
<button onClick={() => {navigate("/dashboard");}} >
Dashboard
</button>
In a react class component use <Navigate>. From the react router docs:
A <Navigate> element changes the current location when it is rendered. It's a component wrapper around useNavigate, and accepts all the same arguments as props.
Try creating a reusable functional Component like a simple button and you can use it in your class component.
import React from "react";
import { useNavigate } from "react-router-dom";
const NavigateButton = ( { buttonTitle, route,isReplaced}) => {
const navigate = useNavigate();
return (
<button
className = "btn btn-primary"
onClick = { () => {
navigate( route , {replace:isReplaced} )
}}
>
{buttonTitle}
</button>;
);
});
export default NavigateButton;
After this, you can use NavigateButton in any of your class Components. And it will work.
<NavigateButton title = {"Route To"} route = {"/your_route/"} isReplaced = {false}/>
Found this explanation from the GitHub react-router issue thread, this explained how to use react-router 6 with class components
https://github.com/remix-run/react-router/issues/8146
I got this code from the above issue explanation
import React,{ Component} from "react";
import { useNavigate } from "react-router-dom";
export const withNavigation = (Component : Component) => {
return props => <Component {...props} navigate={useNavigate()} />;
}
//classComponent
class LoginPage extends React.Component{
submitHandler =(e) =>{
//successful login
this.props.navigate('/dashboard');
}
}
export default withNavigation(LoginPage);
If you need to use params for data fetching, writing a logic in your ClassComponent and render component depending on them, then create wrapper for your ClassComponentContainer
import { useLocation, useParams } from 'react-router-dom';
import ClassComponentContainer from './ClassComponentContainer';
export default function ClassComponentWrap(props) {
const location = useLocation();
const params = useParams();
return <ClassComponentContainer location={location} params={params} />
}
after it just use params in ClassComponent which is in props
import React from 'react';
import { connect } from 'react-redux';
import axios from 'axios';
import PresentationComponent from './PresentationComponent';
class ClassComponent extends React.Component {
componentDidMount() {
let postID = this.props.params.postID;
axios.get(`https://jsonplaceholder.typicode.com/posts/${postID}`)
.then((response) => {console.log(response)})
}
render() {
return <PresentationComponent {...this.props} />
}
}
const mapStateToProps = (state) => {...}
const mapDispatchToProps = (dispatch) => {...}
const ClassComponentContainer = connect(mapStateToProps, mapDispatchToProps)(ClassComponent);
export default ClassComponentContainer;
and use ClassComponentWrap component in Route element attribute
import { BrowserRouter, Route, Routes } from "react-router-dom";
import ClassComponentWrap from './components/ClassComponentWrap';
export default function App(props) {
return (
<BrowserRouter>
<Routes>
<Route path="/posts/:postID?" element={<ClassComponentWrap />} />
</Routes>
</BrowserRouter>
);
}
Here is my solution:
import React, { Component } from "react";
import { useNavigate } from "react-router-dom";
class OrdersView extends Component {
Test(props){
const navigate = useNavigate();
return(<div onClick={()=>{navigate('/')}}>test{props.test}</div>);
}
render() {
return (<div className="">
<this.Test test={'click me'}></this.Test>
</div>);
}
}

Cannot access {variable-name} before initialization (React import)

I'm getting a Reference Error
Cannot access 'STRUCTURE_COLUMN_ID' before initialization
I have a structureColumn file which contains the following:
import React from 'react';
import Column from './column';
export const STRUCTURE_COLUMN_ID = 'Structure';
export default class StructureColumn extends Column {
constructor(name) {
super(STRUCTURE_COLUMN_ID);
}
clone() {
return new StructureColumn(this.name);
}
getKey() {
return STRUCTURE_COLUMN_ID;
}
}
When trying to access StructureColumn class or STRUCTURE_COLUMN_ID variable from a component I get the mentioned error.
The component looks the following:
import React from 'react';
import { List, ListItem, Tooltip } from '#material-ui/core';
import { STRUCTURE_COLUMN_ID } from '../../../../models/structureColumn';
console.log(STRUCTURE_COLUMN_ID);
const CustomColumnsList = ({ onSelect }) => {
return (
<List>
{Object.values({}).map(col => (
<Tooltip title={col.description}>
<ListItem onClick={() => onSelect(col.column)} button>
{col.name}
</ListItem>
</Tooltip>
))}
</List>
);
};
export default CustomColumnsList;
I can use the variable inside the functional component body but the thing is I wanted to create a constant variable out of it's scope. Never seen this issue before in React. Someone has an experience dealing with it?

How to turn an Object (data from database) into an array and show one of its keys on the simulator

So I am trying to render my Firebase Database onto my Android simulator and it is showing an error, in which the solution is to convert an object into an array, but I do not know-how.
import _ from 'lodash';
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {FlatList, View, Text} from 'react-native';
import {employeesFetch} from '../actions';
import ListItem from './ListItem';
class EmployeeList extends Component {
componentDidMount(){
this.props.employeesFetch();
}
renderRow(employee) {
return <ListItem employee={employee}/>;
};
render(){
console.log(this.props.employees);
return (
<View>
<FlatList
data={Object.keys(this.props.employees)}
renderItem={this.renderRow}
/>
</View>
);
}
}
const mapStateToProps = state => {
const employees = _.map(state.employees, (val, uid) => {
return { ...val, uid };
});
return { employees };
};
export default connect(mapStateToProps, { employeesFetch })(EmployeeList);
That mapStateToProps helper is supposed to create a variable called employees (if I am not wrong, I am just learning to code) but when I write that down like this:
<FlatList
data=employees
renderItem={this.renderRow}
/>
It says that employees is not defined. Just in case, I will add the ListItem file to show what I have done there.
import React, {Component} from 'react';
import {Text} from 'react-native';
import {CardSection} from './common';
class ListItem extends Component {
render () {
const { name } = this.props.employee;
return (
<CardSection>
<Text style={styles.titleStyle}>
{name}
</Text>
</CardSection>
);
}
}
export default ListItem;
I expect the output to be a list showing the employees (recorded in Firebase Database) on the screen, but is showing an error: Invariant Violation: Objects are not valid as a React child (found: object with keys {name, phone, shift, uid}). If you meant to render a collection of children, use an array instead.

React 16.3 Context API -- Provider/Consumer issues

I have been doing some experiment on React 16.3.1 ContextAPI. and I encountered into something that I couldn't fathom. I was hoping I could use your help.
Note: The problem have been solved but, its not the solution I am looking for.
Let start with first experiment on multiple components within same file Index.js.
import React, { Component, createContext } from 'react';
const { Provider, Consumer } = createContext();
class AppProvider extends Component {
state = {
name: 'Superman',
age: 100
};
render() {
const increaseAge = () => {
this.setState({ age: this.state.age + 1 });
};
const decreaseAge = () => {
this.setState({ age: this.state.age - 1 });
};
return (
<Provider
value={{
state: this.state,
increaseAge,
decreaseAge
}}
>
{this.props.children}
</Provider>
);
}
}
class Person extends Component {
render() {
return (
<div className="person">
<Consumer>
{context => (
<div>
<p>I'm {context.state.name}</p>
<p>I'm {context.state.age}</p>
<button onClick={context.increaseAge}>
<span>+</span>
</button>
<button onClick={context.decreaseAge}>
<span>-</span>
</button>
</div>
)}
</Consumer>
</div>
);
}
}
class App extends Component {
render() {
return (
<AppProvider>
<div className="App">
<p>Imma Apps</p>
<Person />
</div>
</AppProvider>
);
}
}
export default App;
As result, this render out perfect without any error. I am able to see name (Superman) and age (100). I am able to increase and decrease age by 1.
As you can see, I have imported {createContext} from react then created {Provider, Consumer}. Wrapped <Provider> with state value and <Consumer>.
Next Experiment, was exact copy each component from index.js and paste them separately into their own files.
AppProvider.js
import React, { Component, createContext } from 'react';
const { Provider, Consumer } = createContext();
class AppProvider extends Component {
state = {
name: 'Superman',
age: 100
};
render() {
const increaseAge = () => {
this.setState({ age: this.state.age + 1 });
};
const decreaseAge = () => {
this.setState({ age: this.state.age - 1 });
};
return (
<Provider
value={{
state: this.state,
increaseAge,
decreaseAge
}}
>
{this.props.children}
</Provider>
);
}
}
export default AppProvider;
Person.js
import React, { Component, createContext } from 'react';
const { Provider, Consumer } = createContext();
class Person extends Component {
render() {
return (
<div className="person">
<Consumer>
{context => (
<div>
<p>I'm {context.state.name}</p>
<p>I'm {context.state.age}</p>
<button onClick={context.increaseAge}>
<span>+</span>
</button>
<button onClick={context.decreaseAge}>
<span>-</span>
</button>
</div>
)}
</Consumer>
</div>
);
}
}
export default Person;
App.js
import React, { Component, createContext } from 'react';
const { Provider, Consumer } = createContext();
class App extends Component {
render() {
return (
<AppProvider>
<div className="App">
<p>Imma Apps</p>
<Person />
</div>
</AppProvider>
);
}
}
export default App;
As result, I am getting error - TypeError: Cannot read property 'state' of undefined.
I am unable to grasp what the exactly error was.. All I did was copy and paste each into files without changing any syntax.
Although, Alternative method was to create a new file and add syntax following...
Context.js
import { createContext } from 'react';
const Context = createContext();
export default Context;
Then go into each files (AppProvider.js. Person.js and App.js) and replace...
import React, { Component, createContext } from 'react';
const { Provider, Consumer } = createContext();'
...into...
import Context from './Context.js';. Also replace... <Provider> into <Context.Provider> and <Consumer> into <Context.Consumer>.
And this killed the error. However, this is not the solution I am looking for. I wanted to use <Provider> tag instead of <Context.Provider>.
Question is, Why am I getting this error?
Why am I unable to use this method...
import React, { Component, createContext } from 'react';
const { Provider, Consumer } = createContext();'
for each components in separate files so I could use <Provider> tag ?
Are there any way around to get the solution I'm looking for?
Your help is appreciated and Thanks in advance.
Your are getting TypeError: Cannot read property 'state' of undefined.
Beacuse every time you call const { Provider, Consumer } = createContext(); it creates a new object, this object need to be exported in order for consumers to consume this specific object.
So in person.js
when you try doing {context.state.age} it really does not have state on this object, you just created a new Context which is empty or rather with React internal methods and properties.
So in order to consume the same object just export it, like you did in Context.js and instead of doing:
import { createContext } from 'react';
const Context = createContext();
export default Context;
replace to:
import { createContext } from 'react';
const { Provider, Consumer } = createContext();
export { Consumer, Provider };
Then when you want to use it in other files ( meaning import it ) just call:
import { Consumer, Provider } from './Context.js';

Categories