I am trying to fetch content from server on every Router.
Navigation.js
import { Component } from "react";
import { Link } from "react-router-dom";
export class Navigation extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
all_nav: [
{
menu_name: "News 1",
menu_items_id: "31"
},
{
menu_name: "News 2",
menu_items_id: "32"
},
{
menu_name: "News 3",
menu_items_id: "33"
}
]
};
}
render() {
return (
<ul>
{this.state.all_nav.map((name, key) => (
<li key={key}>
<Link to={`news/${name.menu_items_id}`}>{name.menu_name}</Link>
</li>
))}
<hr />
<li>
<Link to="/">Home</Link>
</li>
</ul>
);
}
}
Navigation.js contains list of navigation that is used to route the content.
Index.js
import React from "react";
import { render } from "react-dom";
import { BrowserRouter, Route, Switch, Link } from "react-router-dom";
import { App } from "./App";
import { NewsList } from "./NewsList";
window.React = React;
render(
<BrowserRouter>
<div>
<Route exact path="/" title="App Title" render={props => <App />} />
<Route path="/news/:filter" render={props => <NewsList {...props} />} />
</div>
</BrowserRouter>,
document.getElementById("root")
);
NewsList.js
import { Component } from "react";
import {Navigation} from './Navigation'
export class NewsList extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
post_record: []
};
}
componentDidMount() {
this.setState({ loading: true });
// console.log(this.props.match.params.filter)
Promise.all([
fetch("https://binodgiri.com.np/api/news_listing.php", {
method: "POST",
headers: {
Accept: "application/json"
},
body: JSON.stringify({
param: this.props.match.params.filter
})
})
])
.then(([all_post_api]) => Promise.all([all_post_api.json()]))
.then(([post_data]) =>
this.setState({
loading: false,
post_record: post_data
})
);
}
render() {
return (
<div>
<h1>Hello Content</h1>
{
this.state.post_record.menu_name
}
<hr />
<Navigation />
</div>
);
}
}
NewsList.js is main rendering section, according to :filter id the content should be fetched from server "https://binodgiri.com.np/api/news_listing.php"
What I really want to do is::
If I click News 1 or anyother News at first. It renders perfectly. And if I want to Navigation to News 2 or other Nothing Happens
Here is the sandbox link :
https://codesandbox.io/s/vykxvp17j7
Thank-you in advance.
You should add this logic.
Since componentDidMount is already mounted on first link click, it wont be called second time, hence you need to add following lifecycle function. (This is the quick fix, you can also rearrange the your logic for better performance.)
componentDidUpdate(prevProps) {
if (this.props.match.params.filter !== prevProps.match.params.filter) {
Promise.all([
fetch("https://binodgiri.com.np/api/news_listing.php", {
method: "POST",
headers: {
Accept: "application/json"
},
body: JSON.stringify({
param: this.props.match.params.filter
})
})
])
.then(([all_post_api]) => Promise.all([all_post_api.json()]))
.then(([post_data]) =>
this.setState({
loading: false,
post_record: post_data
})
);
}
}
In Navigation.js
<Link to={`/news/${name.menu_items_id}`}>{name.menu_name}</Link>
Related
I'm new to React and I am setting up a small project. I am using a NodeJS server that answers to my request and I am trying to redirect the user after an successful login. I dispatch an action and update my redux store with the user information, that is working correctly. But when I try to redirect him I either get no errors and nothing happens or the URL changes but no component renders.
BTW in LoginForm.js I was trying to return a redirect after many fails by trying to add a callback with history object to my action.
So here is my code
App.js
import React, { Component } from 'react';
import LoginPage from './login/LoginPage';
import LandingPage from './landingpage/landing.page';
import ProtectedRoute from './protected/ProtectedRoute';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import PageNotFound from './common/pageNotFound';
class App extends Component {
render() {
return (
<Router >
<Switch>
<Route path="/login" component={() => <LoginPage />} />
<ProtectedRoute path="/" component={LandingPage} />
<Route component={() => <PageNotFound />} />
</Switch>
</Router>
)
}
}
export default App;
LoginPage.js
import React, { Component } from 'react'
import LoginForm from './LoginForm';
import PropTypes from 'prop-types'
import { connect } from 'react-redux';
import { login } from '../../actions/authActions';
import { withRouter } from "react-router";
class LoginPage extends Component {
render() {
const { login, userLoading, history } = this.props;
return (
<div>
<h1>Login in here</h1>
<LoginForm login={login} isLoading={userLoading} history={history} />
</div>
)
}
}
LoginPage.propTypes = {
login: PropTypes.func.isRequired
}
function mapStateToProps(state) {
return {
userLoading: state.auth.isLoading
}
}
export default connect(mapStateToProps, { login })(withRouter(LoginPage));
LoginForm.js
import React, { Component } from 'react'
import TextInput from '../common/TextInput';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Redirect } from 'react-router-dom';
class LoginForm extends Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: '',
redirect: false,
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({
[event.target.name]: event.target.value
})
}
handleSubmit(event) {
event.preventDefault();
this.setState({ error: null });
this.props.login(this.state);
this.setState({
redirect: true
})
}
render() {
const { isLoading, isAuth } = this.props;
const { redirect } = this.state;
console.log(redirect, isAuth)
if (redirect && isAuth) {
return <Redirect to="/" />
}
else {
return (
<form onSubmit={this.handleSubmit}>
<TextInput type="email" name="email" label="Email" onchange={this.handleChange} />
<TextInput type="password" name="password" label="Password" onchange={this.handleChange} />
{isLoading && <p>We are loggin you in</p>}
<button disabled={isLoading} type="submit">Log in</button>
</form>
)
}
}
}
const mapStateToProps = (state) => {
return {
isAuth: state.auth.isAuthenticated
}
}
LoginForm.propTypes = {
login: PropTypes.func.isRequired
}
export default connect(mapStateToProps)(LoginForm);
authActions.js
import {
LOGIN,
LOGIN_SUCCESS,
LOGIN_FAILED,
USER_LOADING,
USER_LOADED,
AUTH_ERROR,
REGISTER_SUCCESS,
REGISTER_FAIL,
} from '../constants';
export function login(payload) {
return dispatch => {
dispatch({ type: USER_LOADING })
setTimeout(function () {
return fetch('http://localhost:3000/user/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
}).then(
(res) => {
dispatch({ type: LOGIN_SUCCESS })
return res.json();
},
(err) => dispatch({ type: LOGIN_FAILED })
).then((data) => {
dispatch({
type: USER_LOADED,
payload: {
token: data.token,
userid: data.userID
}
})
});
}, 1000);
}
}
Since your LoginForm is wrapped with withRouter your can use this.props.history.pushState('/next-route')
I have web application (backend - node.js and frontend react) with two pages. The first page is menu and second page is orders. When I go from orders to menu and press on mouse button back, I get json from web page. Data are load correctly, but page not show html and only json. When I press back in browser, all is correct.
There is my code for Order.
import React, {Component} from 'react';
import MyNavbar from './MyNavbar';
import axios from 'axios';
import {Alert} from 'reactstrap';
import BuildOrder from './BuildOrder';
class Order extends Component {
constructor(props) {
super(props);
this.state = {
orders: [],
visible: false,
};
}
componentDidMount() {
this.getOrders();
if (this.props.location.state && this.props.location.state.alertMessage) {
this.handleUpdateStatus(this.props.location.state.alertColor, this.props.location.state.alertMessage);
}
}
handleUpdateStatus(color, message) {
this.setState({alertColor: color, alertMessage: message});
this.onShowAlert();
}
getOrders() {
const url = '/orders';
axios.get(url).then(response => {
this.setState({orders: response.data})
});
};
onShowAlert = () => {
this.setState({visible: true}, () => {
window.setTimeout(() => {
this.setState({visible: false},
this.props.history.replace({...this.props.location.pathname, state: {}}))
}, 5000)
});
};
toggle() {
this.setState({
visible: !this.state.visible
});
};
handleClickDelete = order => {
axios.delete('/order', { data: { name: order.name, build: order.build } }).then((message) => {
this.getOrders();
this.handleUpdateStatus(message.data.type, message.data.content);
}
)
.catch((err) => {
this.getOrders();
this.handleUpdateStatus('danger', err.message);
});
};
handleClickUpdate(evt, data) {
axios({
method: 'put',
url: '/orders',
headers: {},
data: data
}).then(() => {
this.handleUpdateStatus('success', 'Platba aktualizována');
}
).catch((err) => {
this.handleUpdateStatus('danger', err.message);
});
}
handleClickOrder(evt, data) {
evt.preventDefault();
axios({
method: 'post',
url: '/send/order',
headers: {},
data: data
}).then((message) => {
this.handleUpdateStatus(message.data.type, message.data.content);
}
).catch((err) => {
this.handleUpdateStatus('danger', err.message);
});
}
getOnTop(){
window.scrollTo(0, 0);
};
render() {
const orders = this.state.orders;
let ordersBuildA = [];
let ordersBuildB = [];
let handleClickDelete = this.handleClickDelete;
let handleClickUpdate = this.handleClickUpdate.bind(this);
let handleClickOrder = this.handleClickOrder.bind(this);
let getOnTop = this.getOnTop;
return (
<div>
{orders.filter(order => order.build === 'A').forEach(order => ordersBuildA.push(order))}
{orders.filter(order => order.build === 'B').forEach(order => ordersBuildB.push(order))}
<MyNavbar/>
<Alert className="text-center" color={this.state.alertColor} isOpen={this.state.visible}
toggle={this.toggle.bind(this)}>
{this.state.alertMessage}
</Alert>
<div className="container">
<BuildOrder build="A" orders={ordersBuildA} handleClickDelete={handleClickDelete.bind(this)}
handleClickUpdate={handleClickUpdate.bind(this)} handleClickOrder={handleClickOrder.bind(this)} getOnTop={getOnTop.bind(this)}/>
<BuildOrder build="B" orders={ordersBuildB} handleClickDelete={handleClickDelete.bind(this)}
handleClickUpdate={handleClickUpdate.bind(this)} handleClickOrder={handleClickOrder.bind(this)} getOnTop={getOnTop.bind(this)}/>
</div>
</div>
);
}
}
export default Order;
And index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import NotFound from './componets/NotFound';
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom';
import Order from './componets/Order';
const RouterMapping = () => (
<Router>
<Switch>
<Route path='/' exact component={App}/>
<Route path='/orders' exact component={Order}/>
<Route exact component={NotFound}/>
</Switch>
</Router>
);
ReactDOM.render(
<RouterMapping/>,
document.getElementById('root')
);
module.hot.accept();
serviceWorker.unregister();
EDIT.
There is full project
https://bitbucket.org/mjedle/obedy_docker/src/master/
The problem was, that I call the same url for frontend and backend. When I added /api/orders to backend all is ok.
React - Page doesn't display when using the browser back button
I'm trying to figure out how to extract data from a form with radio buttons when clicking the "buy now" button. I know in react you can grab the values and set state, but I'm trying to use an ajax call which I'm not 100% on how to set it up. I trying follow the axios github page but I'm more confused. I am missing a dependency because that's what my error message said. If anyone could clear up how this is set up that would be great!
This is my error message:
Here is my App.js where my click function lives:
import React, { Component } from "react";
import { BrowserRouter, Route, Redirect } from "react-router-dom";
import axios from "axios";
import eyewearData from "./data/eyewear.json";
import Header from "./components/Header";
import EyewearContent from "./components/EyewearContent";
import Buy from "./components/Buy";
import SelectEyewearPage from "./components/SelectEyewearPage";
import './App.css';
class App extends Component {
constructor() {
super()
this.state = {
eyewearData: eyewearData,
img: {},
title: "",
name: "",
price: "",
description: "",
sizes: [],
purchaseVal: "",
redirect: false
}
this.handleClick = this.handleClick.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.setState({
purchaseVal: e.target.value
});
}
handleSubmit(e) {
e.preventDefault();
const purchaseVal = {
purchaseVal: this.state.purchaseVal
};
axios.post("/selecteyewear",
{ purchaseVal }).then(function (response) {
console.log(response);
}).catch(function (error) {
console.log(error.request);
console.log(error.message);
});
};
handleClick(data) {
this.state.eyewearData.eyewear.filter( eyewear => {
console.log(eyewear.id === data.target.getAttribute("data-id") && this.setState({
img: eyewear.images,
title: eyewear.brand,
name: eyewear.name,
price: eyewear.price,
description: eyewear.description,
sizes: eyewear.sizes
}))
})
};
render() {
return (
<BrowserRouter>
<div>
<Header />
<Route exact path="/" render={() => <EyewearContent
eyewearData={this.state.eyewearData}
handleClick={this.handleClick}
img={this.state.img}
title={this.state.title}
name={this.state.name}
price={this.state.price}
description={this.state.description}
sizes={this.state.sizes}
handlePurchaseEvent={this.handlePurchaseEvent} />} />
<Route exact path="/buy" component={Buy} />
</div>
</BrowserRouter>
);
}
}
export default App;
Here is the file where my form lives:
import React from "react";
const EyewearPurchaseBtn = (props) => (
<div>
<form className="eyewear-purchase-form" onSubmit={props.handleSubmit}>
<h3>Select Size</h3>
{
props.sizes.map( size => (
<div key={size} >
<input onChange={props.handleChange} className="radio-btn" type="radio" name="purchaseVal" value={size} />
<label className="eyewear-purchase-label">{size}</label><br/>
</div>
))
}
<button className="buy-now">Buy Now</button>
</form>
</div>
);
export default EyewearPurchaseBtn;
You need to give the full path of axios in the import statement
import axios from 'axios';
P.S. just reinstall axios via npm/yarn if it is still throwing you error
I'm doing this in App.js:
<Route path="/discover/:query" component={Discover}/>
Then I'm trying to access the URL parameters in Discover:
componentDidMount() {
alert(this.props.match); // undefined
}
I've tried many other ways, like: alert(this.match); or alert(match);. They are all undefined!
What am I doing wrong? I'm following the docs as far as I can tell.
I'm running React version 16.3.2.
EDIT:
All of App.js:
import React, { Component } from 'react';
import './styles/app.css';
import { Route } from 'react-router-dom';
import Welcome from './welcome';
import Discover from './discover';
import MySearches from './my-searches';
import Login from './login';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
// import Database from './database';
class App extends Component {
constructor(props) {
super(props)
this.state = {
title: '',
}
}
render() {
return (
<div className="App">
<header className="App-header">
{/* <Route path="/" component={Login}/> */}
<Route exact path="/" component={Welcome}/>
<Route path="/discover/:query" component={Discover}/>
<Route path="/my-searches" component={MySearches}/>
{/* <Route path="/database" component={Database}/> */}
</header>
</div>
);
}
}
export default App;
All of discover.js:
import React from 'react';
import Map from './map';
import Search from './search';
import SentimentContainer from './sentiment';
import { Steps } from 'intro.js-react';
import ButtonImportant from '../components/button-important';
import { modelInstance } from '../model/model';
import DrawingAnimation from '../components/intro-drawing-animation'
import 'intro.js/introjs.css';
import '../styles/discover.css';
import '../styles/search.css';
class DiscoverContainer extends React.Component {
constructor(props){
super(props);
this.state = {
status: 'INITIAL',
//Intro.js
initialStep: 0,
introState: 'INITIAL',
steps: [
{
element: '.sentiment-pie',
intro: "This app shows people's sentiment towards subjects based on tweets.</br> <h5><ButtonImportant><a target='_blank' href='https://en.wikipedia.org/wiki/Sentiment_analysis'>What is Sentiment Analysis?</a></ButtonImportant></h5> ",
},
{
element: '#searchInput',
intro: 'You can search for subjects here',
},
{
element: '.date',
intro: 'You can look for tweets in the past 7 days',
},
{
element: '.location',
intro: 'Type in place names or interact with the map to look for tweets in specific locations',
},
{
element: '.sentiment-tweet',
intro: 'The tweets will be displayed here',
},
{
element: '.createPDF',
intro: 'Finally you can export the data in a PDF',
},
],
}
}
componentDidMount() {
console.log("props:");
console.log(this.props.locationl); // undefined
}
handleStatusChange = newStatus => {
this.setState({
status: newStatus
});
}
onExit = () => {
this.setState(() => ({
stepsEnabled: false,
introState: 'INITIAL'
}));
};
toggleSteps = () => {
this.setState(prevState => ({ stepsEnabled: !prevState.stepsEnabled }));
// this.onAfterChange(prevState);
};
onAfterChange = nextStepIndex => {
if (nextStepIndex === 0 && this.state.status !=='LOADED') {
this.setState({
status: 'LOADED'
})
// this.step.updateStepElement(nextStepIndex);
}
else if (nextStepIndex === 3) {
this.setState({
introState: 'MAP'
})
// this.step.updateStepElement(nextStepIndex);
}
else{
this.setState({
introState: 'INITIAL'
})
}
}
render () {
const { stepsEnabled, steps, initialStep} = this.state;
let media = null;
switch (this.state.introState) {
case 'INITIAL':
media = null
break;
case 'MAP':
media = <DrawingAnimation />
break;
}
return (
<div className="container-discover">
<Steps
className='intro-steps'
enabled={stepsEnabled}
steps={steps}
initialStep={initialStep}
onExit={this.onExit}
onAfterChange={this.onAfterChange}
/>
<div className="container-discover-top">
<div className='map'>
<Map/>
</div>
<div className="intro">
{media}
<ButtonImportant size="small" text='Explain App' toggleSteps={this.toggleSteps.bind(this)}/>
</div>
<div className='container-search'>
<Search handleStatusChange={this.handleStatusChange}/>
</div>
</div>
<div className="container-discover-bottom">
<SentimentContainer status={this.state.status}/>
</div>
</div>
);
}
}
export default DiscoverContainer;
You need to use the withRouter HOC to access the match props:
export default withRouter(DiscoverContainer);
...
console.log(this.props.match);
I'm new to React and I've made a <Link>to go to next or previous item from dy datas(for example, if i am on user/2 view, previous link go to user/1 and next link go to user/3), the url is correctly changed but the component is not rendered at all and the datas are not reloaded at all.
I've read that it's due to the component not detecting that the children is not changing state so the parent component does not render.
I've tried to use withRouter but I've got a error : You should not use <Route> or withRouter() outside a <Router> and I'm not understanding what I'm doing so if someone has the solution and some explanation to it I would be grateful :)
App.js :
import React, { Component } from 'react';
import {
Route,
Switch,
withRouter,
} from 'react-router-dom';
import HomePage from './pages/home';
import SinglePage from './pages/single';
class App extends Component {
render() {
return (
<Switch>
<div>
<Route exact path="/" component={HomePage} />
<Route path="/:id" component={SinglePage} />
</div>
</Switch>
);
}
}
export default withRouter(App);
Single.js :
import React, { Component } from 'react';
import Details from '../components/details'
import Header from '../components/header'
import { ProgressBar } from 'react-materialize';
class SinglePage extends Component {
constructor(props) {
super(props);
this.state = {
data: { data: null },
}
}
componentDidMount() {
fetch(`http://localhost:1337/${this.props.match.params.id}`)
.then((res) => res.json())
.then((json) => {
this.setState({
data: json,
});
});
}
render() {
const { data } = this.state;
return (
<div>
<h2> SinglePage </h2>
{!data ? (
<ProgressBar />
) : (
<div>
<Header id={this.props.match.params.id} />
<Details item={data} />
</div>
)}
</div>
);
}
}
export default SinglePage;
Header.js :
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Link, withRouter } from 'react-router-dom';
class Header extends Component {
static propTypes = {
item: PropTypes.shape({
data: PropTypes.string.isRequired,
}).isRequired,
}
render() {
const prev = parseInt(this.props.id) - 1
const next = parseInt(this.props.id) + 1
return (
<div>
<Link to="/"> Retour </Link>
<Link to={`/${prev}`}> Précédent </Link>
<Link to={`/${next}`}> Suivant </Link>
</div>
)
}
}
export default Header;
the solution is pretty-simple. All you need to do is make use of componentWillReceiveProps and check if the param updated, if it did fetch the data again
componentDidMount() {
this.getData(this.props.match.params.id);
}
componentWillReceiveProps(nextProps) {
if(this.props.match.params.id !== nextProps.match.params.id) {
this.getData(nextProps.match.params.id);
}
}
getData = (param) => {
fetch(`http://localhost:1337/${params}`)
.then((res) => res.json())
.then((json) => {
this.setState({
data: json,
});
});
}