I have already asked the question already [LINK], And as I did not get any answer I am re-asking, I hope it is not against the rule.
The issue is that when I trigger the request the result that I get is a page markup, However if I re-trigger then everything solves.
The issue appears to be only when I trigger the request as there are no problem when I do the requests at the loading of the page.
I am using react with redux, and redux-thunk as middleware.
This is an image of the response that I get
These are the code for the components:
Action
import { BEGIN_FETCH_MOVIES, FETCHED_MOVIES, FETCH_FAILED_MOVIES } from '../constants';
import axios from 'axios';
//fetch movie
const searchQuery = (url) => {
return dispatch => {
//dispatch begin fetching
dispatch({
type : BEGIN_FETCH_MOVIES,
})
//make a get request to get the movies
axios.get(url)
.then((res) => {
//dispatch data if fetched
dispatch({type : FETCHED_MOVIES, payload : res.data});
})
.catch((err) => {
//dispatch error if error
dispatch({type : FETCH_FAILED_MOVIES});
});
}
//return the result after the request
}
export default searchQuery;
Main component
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { actionSearchMovie, actionSearchSerie } from '../actions'
import DisplayItemMovie from '../components/DisplayItemMovie';
import DisplayItemSerie from '../components/DisplayItemSerie';
import DrPagination from "../components/DrPagination";
import { Layout, Divider, Icon, Spin, Row } from 'antd';
//Home component
class Home extends Component {
constructor(){
super();
this.state = {
moviePage : 1,
seriePage : 1,
urlMovie : '',
urlSerie : ''
}
}
//make request before the render method is invoked
componentWillMount(){
//url
const discoverUrlMovies = 'https://api.themoviedb.org/3/discover/movie?api_key=72049b7019c79f226fad8eec6e1ee889&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&page=1';
//requests
this.fetchMovie(discoverUrlMovies);
}
fetchMovie = ( url ) => {
this.props.actionSearchMovie(url);
}
//handle pagination
handleChangePage = (page) =>{
let url = 'https://api.themoviedb.org/3/discover/movie?api_key=72049b7019c79f226fad8eec6e1ee889&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&page=' + page;
this.setState({
moviePage : page,
urlMovie : url
}, ()=> this.state);
this.fetchMovie(this.state.urlMovie);
}
//render
render() {
const movies = this.props.movies.results; //movies
let displayMovies; //display movies
const antIcon = <Icon type="loading" style={{ fontSize: 24 }} spin />; //spinner
//if movies and series is undefined, display a spinner
if(movies.results === undefined){
displayMovies = <Spin indicator={antIcon} />
}else {
//map through movies and series and then display the items
displayMovies = movies.results.map((movie) => {
return <DisplayItemMovie key = {movie.id} movie = {movie} />
});
}
return (
<div>
<div className='header'>
Home
</div>
<Divider />
<Layout style = {{paddingBottom : '1rem', margin : '0 auto' }}>
<h1 className = 'title'>Movie</h1>
<Row type = 'flex' style = {{flexWrap : 'wrap'}}>
{displayMovies}
</Row>
<DrPagination total = { movies.total_results } page = { this.handleChangePage } currentPage = { this.state.moviePage } /> </div>
)
}
};
const mapStateToProps = (state) => {
return{
movies : state.search_movies,
}
}
export default connect(mapStateToProps, { actionSearchMovie })(Home);
I am not including the code for the reducer but if needed I will post it.
Related
Could someone please tell me how can I add page number to my url. The component is as follows:
/** NPM Packages */
import React, { Component } from "react";
import { connect } from "react-redux";
import { Spinner, Pagination } from "react-bootstrap";
//import styles from "./App.module.css";
/** Custom Packages */
import List from "../List";
//import fetchCategories from "../../../actions/configuration/category/fetchCategories";
import deleteCategory from "../../../actions/configuration/category/deleteCategory";
import API from "../../../../app/pages/utils/api";
class Category extends Component {
constructor(props) {
super(props);
this.state = {
mesg: "",
mesgType: "",
isLoading: true,
total: null,
per_page: null,
current_page: 1,
pdata: []
};
this.fetchCategoriesAPI = this.fetchCategoriesAPI.bind(this);
}
fetchCategoriesAPI = async pno => {
await API.get("categories?offset=" + (pno.index+1))
.then(res => this.setState({ pdata: res.data }))
.then(() => this.props.passToRedux(this.state.pdata))
.catch(err => console.log(err));
};
componentDidMount = async () => {
const { state } = this.props.location;
if (state && state.mesg) {
this.setState({
mesg: this.props.location.state.mesg,
mesgType: this.props.location.state.mesgType
});
const stateCopy = { ...state };
delete stateCopy.mesg;
this.props.history.replace({ state: stateCopy });
}
this.closeMesg();
await this.fetchCategoriesAPI(1);
this.setState({ isLoading: false });
};
onDelete = async id => {
this.props.removeCategory(id);
await deleteCategory(id).then(data =>
this.setState({ mesg: data.msg, mesgType: "success" })
);
this.closeMesg();
};
closeMesg = () =>
setTimeout(
function() {
this.setState({ mesg: "", mesgType: "" });
}.bind(this),
10000
);
/** Rendering the Template */
render() {
let activePage = this.state.pdata.currPage;
let items = [];
let totalPages = Math.ceil(this.state.pdata.totalCount / 10);
for (let number = 1; number <= totalPages; number++) {
items.push(
<Pagination.Item key={number} active={number == activePage}>
{number}
</Pagination.Item>
);
}
const paginationBasic = (
<div>
<Pagination>
{items.map((item,index)=>{
return <p key={index} onClick={() => this.fetchCategoriesAPI({index})}>{item}</p>
})}
</Pagination>
<br />
</div>
);
const { mesg, mesgType, isLoading } = this.state;
return (
<>
{mesg ? (
<div
className={"alert alert-" + mesgType + " text-white mb-3"}
role="alert"
>
{mesg}
</div>
) : (
""
)}
{isLoading ? (
<div className="container-fluid">
<h4
className="panel-body"
style={{ "text-align": "center", margin: "auto" }}
>
Loading
<Spinner animation="border" role="status" />
</h4>
</div>
) : (
<div>
<List
listData={this.props.categories}
listName="category"
_handleDelete={this.onDelete.bind(this)}
/>
{paginationBasic}
</div>
)}
</>
);
}
}
const matchStatestoProps = state => {
return { categories: state.categories };
};
const dispatchStatestoProps = dispatch => {
return {
passToRedux: pload =>
dispatch({ type: "FETCH_CATEGORIES", payload: pload }),
removeCategory: id => dispatch({ type: "DELETE_CATEGORY", payload: id })
};
};
export default connect(matchStatestoProps, dispatchStatestoProps)(Category);
the route is as follows:
<Route exact path="/categories/:page?" component={Category} />
So basically I want the page number to be displayed in the URL. Also if I change the page number, the data should load the corresponding page. Please help me
Could someone please help me out?
In a class component:
Your router will pass match in as a prop. When your component mounts, get this.props.match.params.page and load the data accordingly:
class MyComponent extends React.Component {
componentDidMount () {
// get the 'page' param out of the router props.
// default to 0 if not specified.
const { page = 0 } = this.props.match.params;
// it comes in as a string, parse to int
const p = parseInt(page, 10);
// do whatever you need to do (load data, etc.)
}
}
In a function component:
In a function component, you can get the page param via react-router's useParams hook:
import { useParams } from 'react-router-dom';
function MyComponent () {
const { page } = useParams(); // get the 'page' router param
const p = parseInt(page, 10); // comes in as a string, convert to int
// do whatever you need to do with it
}
If you need prev/next navigation you can deduce those page numbers from the current page.
I made this quick example that demonstrates how to access and use the route's url parameters via react router's useParams hook and how to do it via the match prop with a class component.
You can get page number from props like this:
const matchStatestoProps = (state, ownProps) => {
return { id: ownProps.match.params.id; categories: state.categories };
};
In your routes:
<Route path="/page/:id" component={Page} />
I want to delete specific record which i am in its detailed page and upon delete i want to user to redirect main list page.
Now using this code i am able to delete record but i am getting error
TypeError: Cannot read property 'xxx' of undefined which i am assuming that after deletion of the individual record like #1 it will try to re render the same page and in the fact that deleted record data will not be there in state so it will throw " Cannot read property"
Now my end goal is to move user from this detailed page to list page where i cans see list of persons not able to find after deletion how to redirect user to another page.
In my case i have person list lets say i have two persons in the list with ID =1 and 2.
takes to person detailed page (like http://localhost:3000/persons/1) and there should be deleted and send back user to main list page with updated http://localhost:3000/persons/
Here is my code for
PersonDetailedHeader.jsx
import React, { Component } from 'react'
import { Segment, Image, Item, Header, Button } from 'semantic-ui-react';
import { Link } from 'react-router-dom';
import { deletePerson } from '../personsActions';
import { connect } from 'react-redux';
const mapState = (state) => ({
persons: state.persons,
})
const actions = {
deletePerson
}
class PersonsDetailedHeader extends Component {
handleDeletePerson = personID => {
this.props.deletePerson(personID);
}
render() {
const { person } = this.props;
const personImageStyle = {
filter: 'brightness(30%)'
};
const personImageTextStyle =
{
position: 'absolute',
bottom: '5%',
left: '5%',
width: '100%',
height: 'auto',
color: 'white'
};
return (
<Segment.Group>
<Segment basic attached="top" style={{ padding: '0' }}>
<Image src={person.ImageURL} size="large" style={personImageStyle} />
<Segment basic style={personImageTextStyle}>
<Item.Group>
<Item>
<Item.Content>
<Header
size="huge"
content={person.FullName}
style={{ color: 'white' }}
/>
<p>BirthDate: <strong>{person.BirthDate}</strong></p>
<p>
Sex: <strong>{person.Sex}</strong>
</p>
</Item.Content>
</Item>
</Item.Group>
</Segment>
</Segment>
<Segment attached="bottom">
<Button color="red" onClick={() => this.handleDeletePerson(person.id)}>
Delete Person</Button>
<Button color="orange" floated="right" as={Link} to={`/managePerson/${person.id}`}>
Manage Person
</Button>
</Segment>
</Segment.Group >
)
}
}
export default connect(mapState, actions)(PersonsDetailedHeader);
PersonList.jsx
import React, { Component, Fragment } from 'react'
import PersonListItem from './PersonListItem';
class PersonList extends Component {
render() {
const { persons, deletePerson } = this.props;
return (
<Fragment>
{persons.map(person => (
<PersonListItem
key={person.id}
person={person}
deletePerson={deletePerson} />
))}
</Fragment>
)
}
}
export default PersonList
PersonDashboard.jsx
import React, { Component } from 'react'
import { Grid } from 'semantic-ui-react';
import PersonList from './PersonList/PersonList';
import { connect } from 'react-redux';
import { createPerson, updatePerson, deletePerson } from './personsActions';
import LoadingComponent from '../../app/layout/LoadingComponent';
const mapState = (state) => ({
persons: state.persons,
loading: state.async.loading
})
const actions = {
createPerson,
updatePerson,
deletePerson
}
class PersonDashboard extends Component {
handleDeletePerson = personID => {
this.props.deletePerson(personID);
}
render() {
const { persons, loading } = this.props;
if (loading) return <LoadingComponent />
return (
<Grid>
<Grid.Column width={12}>
<PersonList persons={persons} deletePerson={this.handleDeletePerson} />
</Grid.Column>
<Grid.Column width={3}>
</Grid.Column>
</Grid>
)
}
}
export default connect(mapState, actions)(PersonDashboard)
Now following code are for my Person reducers which are as following
personsActions.js
import { CREATE_PERSON, UPDATE_PERSON, DELETE_PERSON, FETCH_PERSON } from "./personsConstants";
import { asyncActionStart, asyncActionFinish, asyncActionError } from "../async/asyncActions";
import { fetchSampleData } from "../../app/data/mockApi";
import { toastr } from "react-redux-toastr";
export const createPerson = (person) => {
return async dispatch =>{
try{
dispatch({
type: CREATE_PERSON,
payload:{
person
}
})
toastr.success('Sucess!','Person has been created');
}
catch(error){
toastr.error('Opps !','Something went Wrong');
}
};
};
export const updatePerson = (person) =>{
return async dispatch =>{
try{
dispatch({
type: UPDATE_PERSON,
payload:{
person
}
})
toastr.success('Upadate Sucess !','Person has been Sucessfully updated');
}
catch(error){
toastr.error('Opps !','Something went wrong while update');
}
}
}
export const deletePerson = (personId) =>{
return async dispatch =>{
try{
dispatch({
type: DELETE_PERSON,
payload:{
personId
}
})
toastr.success('Sucess !','Person Deleted sucessfully');
}
catch(error){
toastr.error('Opps !','Something went wrong while delete person');
}
}
}
export const loadPersons = () =>{
return async dispatch =>{
try{
dispatch(asyncActionStart())
const persons = await fetchSampleData();
dispatch({type:FETCH_PERSON,payload:{persons}})
dispatch(asyncActionFinish())
}
catch(error){
console.log(error);
dispatch(asyncActionError())
}
}
}
personReducer.js
import {createReducer} from '../../app/common/utils/reducerUtils';
import { CREATE_PERSON, UPDATE_PERSON, DELETE_PERSON, FETCH_PERSON } from './personsConstants';
const initState =[]
const createPerson = (state,payload) =>{
return [...state,payload.person]
}
const updatePerson = (state,payload) =>{
return [
...state.filter(person => person.id !== payload.person.id),payload.person
]
}
const deletePerson = (state,payload) =>{
return [
...state.filter(person => person.id !== payload.personId)
]
}
const fetchPersons = (state,payload) =>{
return payload.persons
}
export default createReducer(initState,{
[CREATE_PERSON]: createPerson,
[UPDATE_PERSON]: updatePerson ,
[DELETE_PERSON]: deletePerson,
[FETCH_PERSON]: fetchPersons
} )
Here is screen shot of Detailed page.
One possible solution is to have a check in the render() if person (or personId) exists? if so, render the detailed page of the person as you usually do... if person does not exists, then redirect it to the desired page (e.g. main list page).
So, the delete logic does not have redirect logic, just delete the person.
Here on button click event I am uploading some data in server . I am using mutation for this . And after the response I have to navigate to previous screen and I don't want to refresh the page or re-render the any life cycle methods of the screen where i am directing .
I have used this.props.navigation.navigate("pagename") ,but by using this some function is getting call .
So I have used "this.props.navigation.goBack()" ,but again same .
I have to go back to previous screen after submitting request to server.
import React, { Component } from 'react';
import { View } from 'native-base';
import {withApollo } from 'react-apollo';
import gql from 'graphql-tag';
import _ from 'lodash';
import OverlaySpinner from '../ui/overlaySpinner';
import AddNoteSection from '../../components/tabs/requestTab/AddNoteSection';
import { handleErrors } from '../../services';
class AddNoteSectionContainer extends Component {
constructor(props) {
super(props);
this.state = {
categoryList: [],
isOpenClose: false,
notes: "",
notesResponse:[]
};
}
addNoteChange = (event) => {
this.setState({
notes: event
}, () => {
});
};
statusTextModification = (currentstatus) => {
var status ="";
if (currentstatus === "Pending"){
status = "P"
}else if(currentstatus === "Close"){
status = "C"
}else{
status = "A"
}
return status;
}
OnButtonClick = async (data) => {
var status = "";
const{navigation}=this.props;
const{workFlowDetails,troubleTicketDetails} =
navigation.state.params.ticketDetailsInfo;
const workAgent_ID = workFlowDetails.currentNextActivity;
const currentStepPosition = workAgent_ID.filter((item) => {
return item._activityStatus === "I"
});
const workAgentID = currentStepPosition.map(currentStepPosition => {
return currentStepPosition._workAgent;
});
let workAgent_id=workAgentID[0];
console.log("Props for note notes",workAgent_id);
if (navigation.state.params.currentStatus === "Pending"){
status = "P"
}else if(navigation.state.params.currentStatus === "Close"){
status = "C"
}else{
status = "A"
}
const mutationObj = `
mutation createIncidentNote{
createIncidentNote(
input:{
status: "${status}",
incidentInitiator: "${data}",
notes: "${this.state.notes}",
userId: "${troubleTicketDetails.LoggedBy}",
workAgentID: "${workAgent_id}",
referenceNumber: "${navigation.state.params.referenceNumber}",
}){
REQUEST_STATUS
ABILLITY_REF_NUM
SUCCESS_MESG_LANG_1
SUCCESS_MESG_LANG_2
}
}
`;
try {
const { data } = await this.props.client.mutate({
mutation: gql(mutationObj)
});
// Here below is the code I am using .
this.props.navigation.goBack()
} catch (e) {
handleErrors(e, this.props.navigation);
console.log('Error in Adding note', e);
}
};
render(){
return(
<View>
<AddNoteSection
{...this.props}
addNoteChange={(text) => this.addNoteChange(text)}
OnButtonClick={(data) => this.OnButtonClick(data)}
/>
{/* {<OverlaySpinner color="#00678F" />} */}
</View>
)
}
}
export default withApollo(AddNoteSectionContainer);
I have a React-Router-Redux application that I built with an expressJS server. Part of this application is authentication using JWT. Aside from protecting Routes, I am trying to create a HoC that will protect it's wrapped component by reaching out to the server and authenticating before displaying the wrapped component. Here is the HoC I have built:
withAuth.js:
import React, { Component } from 'react';
import {connect} from 'react-redux';
import * as actions from '../../store/actions';
export default function (ComposedComponent) {
class Authenticate extends Component {
componentWillMount() {
console.log('will mount');
this.props.authenticate();
}
render() {
const { loading, loaded } = this.props;
return !loading && loaded ? <ComposedComponent {...this.props} /> : null;
}
}
const mapStateToProps = state => {
return {
loading: state.auth.loading,
loaded: state.auth.loaded
};
};
const mapDispatchToProps = dispatch => {
return {
authenticate: () => dispatch(actions.authenticate())
};
};
return connect(mapStateToProps, mapDispatchToProps)(Authenticate)
}
I am using Redux Saga aswell. The authenticate action calls a saga that sets loading to true, loaded to false and reaches out to the server. When the server sends confirmation, loaded is set to true and loading is set to false, aside from a cookie and some data being saved.
It basically works, but the problem is that when I enter a route with this HoC, the authentication process is done twice (HoC's ComponentWillMount is called twice) and I cant figure out why. It happens with a wrapped component that doesnt even reach out to the server or change props on mount/update. What am I missing here?
This is one of the wrapped components that has this problem:
class SealantCustomer extends Component {
state = {
controls: {
...someControls
}
}
shouldComponentUpdate(nextProps) {
if (JSON.stringify(this.props.sealantCustomer) === JSON.stringify(nextProps.sealantCustomer)) return false;
else return true;
}
updateInput = (event, controlName) => {
let updatedControls = inputChangedHandler(event, controlName, this.state.controls);
this.setState({controls: updatedControls});
}
searchCustomer = async (event) => {
event.preventDefault();
this.props.fetchCustomer(this.state.controls.phone.value, this.state.controls.site.value, this.state.controls.name.value);
}
render () {
let sealantCustomer;
if (this.props.loading) {
sealantCustomer = <Loader />;
}
if (!this.props.loading) {
if (!this.props.sealantCustomer) this.props.error ? sealantCustomer = <h3 style={{color: 'salmon'}}>ERROR: {this.props.error}</h3> : sealantCustomer = <h3>Please search for a sealant customer</h3>
else if (this.props.sealantCustomer.length === 0) sealantCustomer = <h3>Found no sealant customers with these details!</h3>
else {
let data = [];
this.props.sealantCustomer.forEach(person => {
...filling data here
})
const columns = [{
...table columns
}]
const keysToSkip = [keys];
sealantCustomer = <ReactTable data={data} columns={columns} defaultPageSize={3} className={['-striped', '-highlight', 'tableDefaults'].join(" ")}
SubComponent={sub component} />
}
}
return (
<div className={classes.sealantCustomerPage}>
<SearchBox controls={this.state.controls} submit={this.searchCustomer} inputUpdate={this.updateInput} name="Sealant Customers" />
<div className={classes.sealantCustomer}>
{sealantCustomer}
</div>
</div>
)
}
};
const mapStateToProps = state => {
return {
loading: state.searches.loading,
error: state.searches.error,
sealantCustomer: state.searches.sealantCustomer
};
};
const mapDispatchToProps = dispatch => {
return {
fetchCustomer: (phone, site, name) => dispatch(actions.searchSealantCustomer(phone, site, name))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(SealantCustomer);
I have a react-select component with options from a axios GET, I want my Car component to display an image from a url stored in the component state when the option is selected.
I am using componentDidMount and componentDidUpdate, however, in componentDidUpdate, this.getImage(capID); keeps firing, how can I prevent this and evoke it once?
import React from "react";
import axios from "axios";
import { Panel } from "react-bootstrap";
export default class CarList extends React.Component {
constructor(props) {
super(props);
this.state = {
imageSrc: ""
};
this.getImage = this.getImage.bind(this);
}
getImage(id) {
axios
.get(`xxx${id}`)
.then(response => {
this.setState({
imageSrc: response.data.url
});
})
.catch(error => {
console.log(error);
});
}
componentDidMount() {
const {
agrNo,
balloon,
bpid,
capID,
dealer,
derivative,
id,
make,
model,
name
} = this.props.car;
this.getImage(capID);
}
componentDidUpdate() {
const {
agrNo,
balloon,
bpid,
capID,
dealer,
derivative,
id,
make,
model,
name
} = this.props.car;
this.getImage(capID);
}
render() {
let car = this.props.car;
const {
agrNo,
balloon,
bpid,
capID,
dealer,
derivative,
id,
make,
model,
name
} = this.props.car;
return (
<div className="car-details">
<Panel header={name}>
<div className="flex-container">
<div className="flex-item">
{this.state.imageSrc && (
<img
src={this.state.imageSrc}
alt={model}
className="car-details__image"
/>
)}
</div>
<div className="flex-item">
<p>{car.Plot}</p>
<div className="car-info">
<div>
<span>Genre:</span> {car.Genre}
</div>
</div>
</div>
</div>
</Panel>
</div>
);
}
}
App:
import React, { Component } from "react";
import logo from "./logo.svg";
import axios from "axios";
import { Alert } from "react-bootstrap";
import AsyncSelect from "react-select/lib/Async";
import CarList from "./CarList";
import "react-select/dist/react-select.css";
import "./App.css";
class App extends Component {
constructor(props) {
super(props);
this.state = {
car: {}
};
}
getCars(e) {
return axios
.get(`xxx${e}`)
.then(response => {
var arr = [];
if (response.data !== undefined) {
var searchResults = response.data.length;
for (var i = 0; i < searchResults; i++) {
arr.push({
label: `${response.data[i].name} - ${response.data[i].id}`,
value: response.data[i].id
});
}
}
return {
options: arr
};
})
.catch(error => {
console.log(error);
});
}
getCar(e) {
axios
.get(`xxx}`)
.then(response => {
this.setState({
car: response.data
});
})
.catch(error => {
console.log(error);
});
}
render() {
const {
car: { id }
} = this.state;
return (
<div className="container">
<AsyncSelect
name="carOwner"
value="ABC"
cacheOptions
defaultOptions
loadOptions={this.getCars}
onChange={this.getCar.bind(this)}
/>
{id ? (
<CarList car={this.state.car} />
) : (
<Alert bsStyle="info">
<p>Enter a surname above to begin...</p>
</Alert>
)}
</div>
);
}
}
export default App;
componentDidUpdate will fire whenever any prop or state for this component has changed (checkout the official docs for more info).
You're changing the state inside the getImage(id) function, and every time that happens, the componentDidUpdate function will fire in your case, which will call the getImage function again, which will then became an infinite loop.
You need to check if the capID prop has changed, in order to decide if you should make the call again or not:
componentDidUpdate(oldProps) {
const {
agrNo,
balloon,
bpid,
capID,
dealer,
derivative,
id,
make,
model,
name
} = this.props.car;
const oldCapID = oldProps.capID;
if (capID !== oldCapID) {
this.getImage(capID);
}
}