how to call a function of another component in react js - reactjs

I have a NavMenu component class like this:
import React, { Component } from "react";
import { Navbar, NavItem, NavLink, OffcanvasBody } from "reactstrap";
import "./styles/NavMenu.css";
import Offcanvas from "react-bootstrap/Offcanvas";
export class NavMenu extends Component {
static displayName = NavMenu.name;
constructor(props) {
super(props);
this.state = {
showOffcanvas: false,
};
this.setShowOffcanvas = this.setShowOffcanvas.bind(this);
this.setHideOffcanvas = this.setHideOffcanvas.bind(this);
}
setShowOffcanvas() {
this.setState({ showOffcanvas: true });
}
setHideOffcanvas() {
this.setState({ showOffcanvas: false });
}
render() {
return (
<>
<Navbar className="sticky-top">
<ul className="left-navbar">
<NavItem>
<NavLink onClick={this.setShowOffcanvas} className="original">
Open Offcanvas
</NavLink>
</NavItem>
</ul>
</Navbar>
<Offcanvas
className="offcanvasPlayer"
show={this.state.showOffcanvas}
onHide={this.setHideOffcanvas}
>
<NavLink
className="text-white card-link close-btn"
data-bs-dismiss="offcanvas"
onClick={this.setHideOffcanvas}
></NavLink>
<OffcanvasBody>{/* offcanvas body here */}</OffcanvasBody>
</Offcanvas>
</>
);
}
}
then my navbar button to open offcanvas in another component like this :
import React, { Component } from "react";
import { NavLink } from "reactstrap";
import "./styles/TopNavbar.css";
export class TopNavbar extends Component {
static displayName = TopNavbar.name;
constructor(props) {
super(props);
this.state = {
showOffcanvas: false,
showPlayer: false,
};
this.setShowOffcanvas = this.setShowOffcanvas.bind(this);
this.setHideOffcanvas = this.setHideOffcanvas.bind(this);
}
setShowOffcanvas() {
this.setState({ showOffcanvas: true });
}
setHideOffcanvas() {
this.setState({ showOffcanvas: false });
}
render() {
return (
<>
<NavLink className="item" onClick={this.setShowOffcanvas}>Opening Offcanvas NavMenu component</NavLink>
</>
);
}
}
but when i'm clicking on the (this.setShowOffcanvas), project show this error:
Uncaught TypeError: Cannot read properties of null (reading 'hide')
at HTMLAnchorElement.<anonymous> (offcanvas.js:254:1)
at HTMLDocument.handler (event-handler.js:118:1)
I swear to God, whatever I did didn't work

Related

react : why is axios api call not using proxy in package.json?

I have a react app that i've made and it is working great in storybook with a mock for data retrieving.
When I switch to yarn start to check the app without mock, the page is loading some components but not the main component (PostPageCardContainer) which is only displaying "loading" (see the code below).
The component which load properly make api calls like this in ComponentDidMount :
axios.get("/api/blog/categories/").then((res) => {
const categories = res.data.results;
this.setState({
categories,
loading: false });
and
axios.get("/api/blog/tags/").then((res) => {
const tags = res.data.results;
this.setState({
tags,
loading: false });
}); }
The components that dont load make an api call like this in COmponentDidMount:
PostPageCard.js:
const pk = this.props.match.params.id;
axios.get(`/api/cms/pages/${pk}/`).then((res) => {
const post = res.data;
this.setState({
post,
loading: false });
}) }
PostDetail.js
axios.get(`/api/cms/pages/${this.props.postPk}/`).then((res) => {
this.setState({
data: res.data,
loading: false
}); });
In the browser console, when i try to load the page i get :
printWarnings # webpackHotDevClient.js:138
:3000/api/cms/pages/6/:1
Failed to load resource: the server responded with a status of 404 (Not Found)
And when i hover the mouse on the link i get http://localhost:3000/api/cms/pages/6.
In fact the react page is being served on localhost:3000 but I have put "proxy": "http://172.20.128.2:8000" in packages.json so my api call go on this adress.
How come some api calls go on the good adress and others dont?
The issue is similar to this : How to set proxy when using axios to send requests? and this Axios not using proxy setting with https and this axios request ignores my proxy and even when hardcoded I can't fetch any data but there is not really a solution except using fetch or restarting the machine
I ve tried to hardcode the proxy in the api call like axios.get(http://172.20.128.2:8000/api/cms/pages/${this.props.postPk}` and removed the proxy line from package.json but then nothing is loading properly...
Here is some sample of the code:
index.js
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import App from "./components/App";
import 'bootstrap/dist/css/bootstrap.css';
import { MemoryRouter } from "react-router-dom";
ReactDOM.render(
<React.StrictMode>
<MemoryRouter initialEntries={["/"]}>
<App/>
</MemoryRouter>
</React.StrictMode>,
document.getElementById("root")
);
App.js
import React from "react";
import { Route, Switch } from "react-router";
import { Container, Row } from "react-bootstrap";
import { BlogPage } from "./BlogPage";
import { PostPage } from "./PostPage";
function App() { return (
<Switch>
<Route path="/post/:id([\d]+)" component={PostPage}/>
<Route path="/tag/:tag/:page([\d]+)?" component={BlogPage}/>
<Route path="/:page([\d]+)?" component={BlogPage}/>
<Route
path="*"
component={() => (
<Container> <Row>
<h1>404</h1> </Row>
</Container> )}
/> </Switch>
); }
export default App;
BlogPage.js
import React from "react";
import { Container, Row } from "react-bootstrap";
import { TopNav } from "./TopNav";
import { Footer } from "./Footer";
import { PostPageCardContainer } from "./PostPageCardContainer";
import { SideBar } from "./SideBar";
class BlogPage extends React.Component { render() {
return (
<div>
<TopNav />
<Container>
<Row>
<PostPageCardContainer {...this.props} />
<SideBar />
</Row>
</Container>
<Footer />
</div> );
} }
export { BlogPage };
Postpagecardcontainer.js
import React from "react";
import axios from "axios";
import { Col } from "react-bootstrap";
import { Link } from "react-router-dom";
import { generatePath } from "react-router";
import _ from 'lodash';
import { PostPageCard } from "./PostPageCard";
class PostPageCardContainer extends React.Component {
constructor(props) {
super(props); this.state = {
posts: [],
pageCount: 0,
pageStep: 2,
};
this.getPosts = this.getPosts.bind(this);
}
componentDidMount() {
this.getPosts();
}
componentDidUpdate(prevProps) {
if (prevProps.location !== this.props.location) {
this.getPosts(); }
}
getCurPage() {
// return the page number from the url
const page = this.props.match.params.page;
return page === undefined ? 1 : parseInt(page);
}
getPrePageUrl() {
const target = _.clone(this.props.match.params);
target.page = this.getCurPage() - 1;
return generatePath(this.props.match.path, target);
}
getNextPageUrl() {
const target = _.clone(this.props.match.params);
target.page = this.getCurPage() + 1;
return generatePath(this.props.match.path, target);
}
getPosts() {
let category = this.props.match.params.category === undefined ? "*" : this.props.match.params.category;
let tag = this.props.match.params.tag === undefined ? "*" : this.props.match.params.tag;
let offset = (this.getCurPage() - 1) * this.state.pageStep;
const url = `/api/blog/posts/?limit=${this.state.pageStep}&offset=${offset}&category=${category}&tag=${tag}`;
axios.get( url).then((res) => {
const posts = res.data.results;
this.setState({
posts,
pageCount: Math.ceil(parseInt(res.data.count) / this.state.pageStep),
});
});
}
render() {
return (
<Col md={8}> {this.state.posts.map((post) => (
<PostPageCard postPk={post.id} key={post.id} /> ))}
<nav aria-label="Page navigation example">
<ul className="pagination">
<li className={
this.getCurPage() <= 1 ? "page-item disabled" : "page-item" }>
<Link to={this.getPrePageUrl()}
className="page-link" >
Previous
</Link>
</li>
<li className={this.getCurPage() >= this.state.pageCount ? "page-item disabled" : "page-item" }>
<Link to={this.getNextPageUrl()}
className="page-link" >
Next
</Link>
</li>
</ul>
</nav>
</Col>
);
}
}
export { PostPageCardContainer };
PostPage.js
import React from "react";
import { Container, Row } from "react-bootstrap";
import { TopNav } from "./TopNav";
import { Footer } from "./Footer";
import { SideBar } from "./SideBar";
import { PostDetail } from "./PostDetail";
class PostPage extends React.Component { render() {
return ( <div>
<TopNav/> <Container>
<Row>
<PostDetail {...this.props} /> <SideBar/>
</Row> </Container> <Footer/>
</div> );
} }
export { PostPage };
PostDetail.js
import React from "react";
import axios from "axios";
import { StreamField } from "./StreamField/StreamField";
class PostDetail extends React.Component {
constructor(props) {
super(props); this.state = {
post: [],
loading: true, };
}
componentDidMount() {
const pk = this.props.match.params.id;
axios.get(`/api/cms/pages/${pk}/`).then((res) => {
const post = res.data;
this.setState({
post,
loading: false });
}) }
render() {
if (!this.state.loading) {
const post = this.state.post;
return (
<div className="col-md-8">
<img src={post.header_image_url.url} className="img-fluid rounded" alt=""/>
<hr />
<h1>{post.title}</h1>
<hr />
<StreamField value={post.body} />
</div> );
}
else {
return <div className="col-md-8">Loading...</div>;
}
}
}
export { PostDetail };
PostPageCard.js
import React from "react";
import { Link } from "react-router-dom";
import axios from "axios";
class PostPageCard extends React.Component {
constructor(props) {
super(props); this.state = {
data: null,
loading: true,
};
}
componentDidMount() {
axios.get(`/api/cms/pages/${this.props.postPk}/`).then((res) => {
this.setState({
data: res.data,
loading: false
}); });
}
renderPost(data) {
const dateStr = new Date(data.pub_date).toLocaleString();
return (
<div className="card mb-4">
<Link to={`/post/${data.id}`}> <img src={data.header_image_url.url} className="card-img-top" alt=""/> </Link>
<div className="card-body">
<h2 className="card-title">
<Link to={`/post/${data.id}`}>{data.title}</Link>
</h2>
<p className="card-text">{data.excerpt}</p>
<Link to={`/post/${data.id}`} className="btn btn-primary">Read More → </Link>
</div>
<div className="card-footer text-muted">Posted on {dateStr}
</div>
</div>
); }
render() {
if (this.state.loading) {
return 'Loading...'; }
else{
return this.renderPost(this.state.data); }
} }
export { PostPageCard };

Getting null when invoking sibling component function

I am implementing a scrolling functionality on the same page when the Contact Us button is clicked. The Contact Us is contained in a child component (MyNavbar); when clicked, it will scroll to a fragment contained in another child component (MyContactForm), which is sibling of MyNavbar.
Here's the parent component:
// App.js
import React, { Component } from 'react';
import MyNavbar from './components/MyNavbar';
import MyContactForm from './components/MyContactForm';
export default class App extends Component {
constructor(props) {
super(props);
...
}
scrollToContactForm = () => {
this.refs.contactForm.scrollTo();
}
render() {
return (
<main>
<MyNavbar onClickToContactUs={ () => this.scrollToContactForm() } />
<MyContactForm ref="contactForm" />
</main>
);
}
}
And here are the two child components, MyNavbar
// MyNavbar.js
import React, { useState } from 'react';
import { Navbar, Nav, NavItem, NavLink } from 'reactstrap';
const MyNavbar = (props) => {
return (
<Navbar>
<Nav>
...
<NavItem>
<NavLink href="/products/"> Products </NavLink>
</NavItem>
<NavItem>
<NavLink href="/services/"> Services </NavLink>
</NavItem>
<NavItem>
<NavLink onClick={ () => props.onClickToContactUs() } href="#"> Contact Us </NavLink>
</NavItem>
</Nav>
</Navbar>
);
}
export default MyNavbar;
and MyContactForm:
// MyContactForm.js
import React, { Component } from 'react';
import { Form, ... } from 'reactstrap';
export default class MyContactForm extends Component {
constructor(props) {
super(props);
this.state = {
email: '',
...
inquiry: ''
};
this.setEmail = this.setEmail.bind(this);
...
this.setInquiry = this.setInquiry.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.myRef = React.createRef();
}
setEmail(event) {
this.setState( { email: event.target.email } );
}
...
setInquiry(event) {
this.setState( { question: event.target.inquiry } );
}
handleSubmit(event) {
alert("Thank you for contacting us. We will respond to you shortly");
event.preventDefault();
}
scrollTo = () => window.scrollTo(0, this.myRef.current.offsetTop);
render() {
return (
<React.Fragment ref={this.myRef} >
<Form onSubmit={this.handleSubmit}>
...
</Form>
</React.Fragment>
);
}
}
The app runs, however when I click Contact Us, I get a message saying
this.myRef.current is null
How can I get this to work?
Here's what worked for me:
I replaced <React.Fragment> with a <div>. Putting the ref in <Form> doesn't work either, as it should be on a DOM node (HTML element, not React component). So MyContactForm.js becomes:
render() {
return (
<div ref={this.myRef} >
<Form onSubmit={this.handleSubmit}>
...
</Form>
</div>
);
}

Not getting props in react component

I'm not getting props in my Nav component. Odd thing is, 'this.props.history.push' is working in my other components.
The same function is working in my other components, but when I try to call the push function, I'm getting 'err in logout TypeError: Cannot read property 'push' of undefined'. The 'this.props' object is logging as '{}'.
Any help is appreciated, thank you.
import React from 'react'
import logo from 'logo.png'
import css from './Nav.module.scss'
import { Link } from 'react-router-dom'
import Cookies from 'js-cookie'
import axios from 'axios'
class Nav extends React.Component {
constructor(props) {
super(props)
this.state = {
loggedIn: false
}
console.log(this.props)
}
_handleLogout = () => {
// const self = this
console.log(this.props)
axios.get('http://localhost:8080/logout', {
withCredentials: true
})
.then(res => {
console.log(res)
console.log('logout')
if (Cookies.get('sid') === undefined) {
this.props.history.push('/')
}
console.log(this.props)
})
.catch(err => {
console.log('err in logout', err)
})
}
render() {
return (
<div className={css.nav}>
<div className={css.leftPart}>
<Link to="/">
<div className={css.brandicon}>
<img src={logo} alt="Logo" />
</div>
<div className={css.brandname}>
somebrand
</div>
</Link>
</div>
<div className={css.rightPart}>
{
Cookies.get('sid') === undefined ?
<Link to="/login">
<div className={css.loginButton}>
Login
</div>
</Link>
:
<div className={css.logoutButton} onClick={this._handleLogout}>
Logout
</div>
}
</div>
</div>
)
}
}
export default Nav
My Nav component is only referenced once in my Layout component:
import React from 'react'
import Nav from 'components/Nav/Nav'
import css from './BasicLayout.module.scss'
class Basic extends React.Component {
render() {
return (
<div className={css.page}>
<Nav />
<div className={css.content}>
{this.props.children}
</div>
</div>
)
}
}
export default Basic
history and location are special props injected by React Router's HOC withRouter
import { withRouter } from 'react-router-dom'
class Nav extends React.Component{
render(){
const { history, location } = this.props
return <div>{`I'm at ${location.pathname}`}</div>
}
}
export default withRouter(Nav)
It works for functional components as well
export const Component = withRouter(({ history, location })) =>(
<div>{`I'm at ${location.pathname}`}</div>
)

React. Redirect by button click in React with react-router-dom

So, I try to understand how can I make right redirection in my app with event clicks? I put the react-router-dom redirect logic into the button event handler, but it does not work.
What is I'm making wrong?
import React, { Component, Fragment } from 'react';
import Preloader from '../Preloader/Preloader'
import preloaderRunner from '../../Modules/PreloaderRunner'
import { Redirect } from 'react-router-dom';
import axios from 'axios';
class LoginPage extends Component {
constructor(props) {
super(props);
this.state = {
navigate: false
}
}
handleClick = () => {
console.log('Button is cliked!');
return <Redirect to="/employers" />
}
render() {
return (
<Fragment>
<Preloader/>
<h1>This is the Auth Page!</h1>
{this.state.navigate === true
? <div>
<div>You already loggined!</div>
<button onClick={this.handleClick}>Go to the Employers List!</button>
</div>
: <div>
<form>
// some code...
</form>
</div>}
</Fragment>
)
}
}
export default LoginPage;
Things returned by a click handler will not be rendered by your component. You have to introduce a new state property that you can set and then render the <Redirect> component when that property contains a path to redirect to:
class LoginPage extends Component {
constructor(props) {
super(props);
this.state = {
navigate: false,
referrer: null,
};
}
handleClick = () => {
console.log('Button is cliked!');
this.setState({referrer: '/employers'});
}
render() {
const {referrer} = this.state;
if (referrer) return <Redirect to={referrer} />;
// ...
}
}
Alternatively instead of rendering your own button with a click handler you could render a <Link> component as suggested by #alowsarwar that will do the redirect for you when clicked.
I believe on click you want to take the user to '/employers' . Then you need to use Link from the react-router-com. Ideally in React events like 'handleClick' should change the state not return a JSX (this is the wrong approach)
import React, { Component, Fragment } from 'react';
import Preloader from '../Preloader/Preloader'
import preloaderRunner from '../../Modules/PreloaderRunner'
import { Redirect, Link } from 'react-router-dom';
import axios from 'axios';
class LoginPage extends Component {
constructor(props) {
super(props);
this.state = {
navigate: false
}
}
handleClick = () => {
this.setState({ navigate: true});
}
render() {
return (
<Fragment>
<Preloader/>
<h1>This is the Auth Page!</h1>
{this.state.navigate === true
? <div>
<div onClick="this.handleClick">If you want to enable link on some event (Sample test case fyr)</div>
{this.state.navigate ? <Link to='/employers'/> : null}
</div>
: <div>
<form>
// some code...
</form>
</div>}
</Fragment>
)
}
}
export default LoginPage;

Can't pass a props to child component inside an iteration

i got a problem when i try to pass a props to child component inside map iteration in my parent component. it always show a message notify that
TypeError: Cannot read property 'props' of undefined
can someone help me figure out what wrong in my code? i already try to pass it through a local state too, then still got an error.
here is my code :
My Parent Component
import React from 'react';
import { TabContent, TabPane, Nav, NavItem, NavLink, Card, Button, CardTitle, CardText, Row, Col } from 'reactstrap';
import classnames from 'classnames';
import PeriodicSetup from './PeriodicSetup';
import PeriodicDataTable from './PeriodicDataTable';
import {connect} from 'react-redux';
import store from '../../store/store';
class SetupPage extends React.Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
this.state = {
activeTab: 0,
};
}
toggle(tab) {
if (this.state.activeTab !== tab) {
this.setState({
activeTab: tab
});
}
}
render() {
return (
<div>
<Nav tabs>
{this.props.SetupTabTitles.map((data, i)=>
<NavItem>
<NavLink className={classnames({ active: this.state.activeTab === i})} onClick={() => {this.toggle(i); }}>
{data.tabTitle}
</NavLink>
</NavItem>
)}
</Nav>
<TabContent activeTab={this.state.activeTab}>
{this.props.SetupTabTitles.map(function(data, i) {
if(data.tabTitle == 'Tasks'){
return (
<TabPane tabId={i}>
test
</TabPane>
)
}else if(data.tabTitle == 'Periodic'){
return (
<TabPane tabId={i}>
<PeriodicSetup />
<PeriodicDataTable periodicData = {this.props.periodicList}/>
</TabPane>
)
}
})}
</TabContent>
</div>
);
}
}
function mapStateToProps(state){
return {
SetupTabTitles : state.component.SetupTabTitles,
periodicList : state.setup.periodicList
};
}
export default connect(mapStateToProps)(SetupPage);
My Child Component :
import React from 'react';
const {Table, Column, Cell} = require('fixed-data-table');
export default class PeriodicDataTable extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
test
</div>
)
}
}
The Condition in my code,
this.props.periodicList
already have an array value, and everything is working well if i commend out my
<PeriodicDataTable periodicData = {this.props.periodicList}/>
or i move it out from the iteration, it works. but i still don't know why it got an error if i put it inside an iteration.
You need pass your map callback function with explicit passing to this that refer to the React component, not the callback function that has no property called props, to be like this:
{this.props.SetupTabTitles.map(this.renderTabTitles.bind(this))}
then add a method in your class as follow:
renderTabTitles(data, i) {
if(data.tabTitle == 'Tasks') {
return (
<TabPane tabId={i}>
test
</TabPane>
)
} else if(data.tabTitle == 'Periodic') {
return (
<TabPane tabId={i}>
<PeriodicSetup />
<PeriodicDataTable periodicData = {this.props.periodicList}/>
</TabPane>
)
}
}

Resources