i have applied pagination in react-redux process, and trying to get query parameter in "mapStateToProps" function but getting below error -
calling browser url is - http://localhost:3000/blog-list?page_no=1
here is my component's code snippet -
import React from 'react';
import StaticLayout from '../Layout/StaticLayout';
import { getBlogList } from '../actions/signupActions';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { Pagination } from 'react-bootstrap';
import { push } from 'react-router-redux';
class BlogList extends React.Component {
constructor(props){
super(props);
document.title = "Blogs";
this.changePage = this.changePage.bind(this);
}
componentDidMount() {
this.props.getBlogList();
}
render(){
//===pagination variable========
const per_page = 1;
let pages = 0;
if(this.props.blogListData !== undefined){
pages = Math.ceil(this.props.blogListData.count / per_page) ;
}
const current_page = this.props.page;
const start_offset = (current_page - 1) * per_page;
let start_count = 0;
//===End pagination variable========
return(
<StaticLayout>
<html content with require list />
<Pagination className="users-pagination pull-right" bsSize="medium" maxButtons={10} first last next prev boundaryLinks items={pages} activePage={current_page} onSelect={this.changePage} />
</StaticLayout>
);
}
changePage(page){
this.props.dispatch(push('/?page_no='+page))
}
}
function mapStateToProps(state){
return {
blogListData: state.UserReducer.blogData,
page: Number(state.routing.locationBeforeTransitions.query.page_no) || 1,
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({getBlogList: getBlogList}, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps) (BlogList);
Please let me know what i am doing wrong, because in console you can see its giving up to routing.locationBeforeTransitions. not 'query'..
You need to configure React-router-redux to be able to use state.routing.locationBeforeTransitions.query.page_no. However there are other ways to do it.
If you are using React-router v4, which I am assuming
You need to make use a separate library that supports query parsing, since its support was withdrawn from react-router v4
You can make use of query-string npm package
You can get the data like
import queryString from 'query-string'
function mapStateToProps(state, ownProps){
var queryParam = queryString.parse(ownProps.location.search);
return {
blogListData: state.UserReducer.blogData,
page: Number(queryParam.get('page_no') || 1,
}
}
Related
This is my URL in react js
http://localhost:3000/meassger/student/1
I want to extract 1 from the URL if it was a functional component I could have used ```useParams``
I am getting this error
TypeError: Cannot read properties of undefined (reading 'params')
componentDidMount() {
console.log(this.props.match.params.id, "");
};
You need to wrap it in withRouter - that injects the URL variables into your props.
You can find an example here: https://stackoverflow.com/a/60316195/13063136
The code:
import React from "react";
import { withRouter } from "react-router";
class ShowTheID extends React.Component {
const { match } = this.props;
componentDidMount() {
console.log(match.params.id)
}
render() {
return <div>{match.params.id}</div>;
}
}
const ShowTheIDWithRouter = withRouter(ShowTheID);
Note: Along with wrapping your component in withRouter you also need to make sure that your route is registered and you have mentioned URL params in your Route path like path="/meassger/student/:id"
Let's assume we have a url like http://localhost:3000/student/:studentId and we need to grab studentId param from this url
In a functional component, we can do it like
import React from 'react';
import { useParams } from 'react-router-dom';
const Student = () => {
const { studentId } = useParams();
return (
<div>StudentId: { studentId }</div>
);
}
export default Student;
In a class based component, we can do it like
import React, { Component } from 'react';
class Student extends Component {
render() {
const { studentId } = this.props.match.params;
return (
<div>StudentId: { studentId }</div>
);
}
}
export default Student;
Alternatively, you can use withRouter HOC. By doing so, you can also access location and history props.
import React, { Component } from 'react';
import { withRouter } from "react-router";
class Student extends Component {
render() {
const { location, history } = this.props;
return (
<React.Fragment>
<div>StudentId: { match.studentId }</div>
<div>Path: {location.pathname}</div>
</React.Fragment>
);
}
}
const StudentWithRouter = withRouter(Student);
In functional component, use
const history = useHistory()
const studentId = history?.location?.pathname.split('/')[3]
In class component, use
const studentId = window.location.href.split('/')[3]
I am using Redux with React to manage my state; however, I get an undefined error whenever I try to access this particular state that looks good and accessible through Redux Chrome Extension (Please See Image Below)
Based on Redux Chrome Extension, memberDetails object should be available to all components through this.props.memberDetails but I get an undefined error.
The class I am accessing memberDetails from is as follows:
import React, {Component} from 'react';
class Details extends Component {
render = () => {
const memberDetails = this.props.memberDetails;
console.log("memberDetails: ", memberDetails);
return (
<div>
</div>
);
}
}
export default Details;
You are supposed to use mapStateToProps function to get the memberDetails value from store.
Look at this. https://react-redux.js.org/using-react-redux/connect-mapstate
import React, {Component} from 'react';
import { connect } from 'react-redux'
class Details extends Component {
render = () => {
const memberDetails = this.props.memberDetails;
console.log("memberDetails: ", memberDetails);
return (
<div>
</div>
);
}
}
function mapStateToProps(state) {
const { memberDetails } = state
return { memberDetails }
}
export default connect(mapStateToProps)(Details)
I want a link in the but then i dont know how to i use it with const
with class it works. can anyone please explain about const components
import React from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router';
const mapStateToProps= state =>{
return { articles :state.articles};
}
const connectedList = ({ articles }) =>(
articles.map(e=>(
<li key={e.id}>{e.title}</li>
))
<Link to="/Form">Form</Link>//// this line is error thrown
);
const List= connect(mapStateToProps)(connectedList);
export default List;
I know I'm do it the wrong way as i have no idea how do i achieve it the right way
You have some syntax errors. Most importantly, your "connectedList" didn't actually return/render anything.
You would have run into other problems because you hadn't wrapped your array of <li>s into a <ul>, but also, a React component render expects there to be only a single element at the top-level -- which is why I wrapped everything in a <div>.
I've cleaned up some items for clarity and used a React class instead of a functional component:
import React from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router';
class List extends React.Component {
render() {
const { articles } = this.props
const articleListItems = articles.map(article => {
return <li key={article.id}>{article.title}</li>
})
return (
<div>
<ul>
{articleListItems}
</ul>
<Link to="/Form">Form</Link>
</div>
)
}
}
const mapStateToProps = state => {
return {
articles: state.articles
}
}
const ListContainer = connect(mapStateToProps, null)(List)
export default ListContainer
In pagination 'onSelect' event I am calling an function that is define outside of render and in component's class. But when event firing below error coming -
BlogList.js:101 Uncaught TypeError: this.props.dispatch is not a function
here is my code snippit -
import React from 'react';
import StaticLayout from '../Layout/StaticLayout';
import { Link } from 'react-router-dom';
import { getBlogList } from '../actions/signupActions';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import dateFormat from 'dateformat';
import { Pagination } from 'react-bootstrap';
import { push } from 'react-router-redux';
import queryString from 'query-string'
class BlogList extends React.Component {
constructor(props){
super(props);
document.title = "Blogs";
this.changePage = this.changePage.bind(this);
}
componentDidMount() {
this.props.getBlogList();
}
render(){
//===pagination variable========
const per_page = 1;
let pages = 0;
if(this.props.blogListData !== undefined){
pages = Math.ceil(this.props.blogListData.count / per_page) ;
}
const current_page = this.props.page;
const start_offset = (current_page - 1) * per_page;
let start_count = 0;
//===End pagination variable========
return(
<StaticLayout>
<blog list related html />
<Pagination className="users-pagination pull-right" bsSize="medium" maxButtons={10} first last next prev boundaryLinks items={pages} activePage={current_page} onSelect={this.changePage} />
</StaticLayout>
);
}
changePage(page){
alert(page);
this.props.dispatch(push('/?page_no='+page))
}
}
function mapStateToProps(state,ownProps){
var queryParam = queryString.parse(ownProps.location.search);
return {
blogListData: state.UserReducer.blogData,
page: Number(queryParam.page_no) || 1,
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({getBlogList: getBlogList}, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps) (BlogList);
Plz let me know what i am doing wrong ?
dispatch is available to the component when you use connect only if you are not overriding it with the a custom function. which in your case is a mapDispatchToProps function. So what you can do is make the push action available as a prop to the component by adding it to the mapDispatchToProps function like
function mapDispatchToProps(dispatch) {
return bindActionCreators({getBlogList: getBlogList, push: push}, dispatch)
}
and use it like
changePage(page){
alert(page);
this.props.push('/?page_no='+page)
}
You can try hoisting the changePage into connect. I find this model easier to read and maintain.
function mapStateToProps(state,ownProps){
var queryParam = queryString.parse(ownProps.location.search);
return {
blogListData: state.UserReducer.blogData,
page: Number(queryParam.page_no) || 1,
}
}
const dispatch = (dispatch, ownProps) => ({
changePage: (page) => {
alert(page);
dispatch(push('/?page_no='+page))
}
});
export default connect(mapStateToProps, dispatch) (BlogList);
I seen 2 ways of doing the same thing but I am not sure what is the proper way.
Component
import React, {Component} from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {selectUser} from '../actions/index'
class UserList extends Component {
renderList() {
return this.props.users.map((user) => {
return (
<li
key={user.id}
onClick={() => this.props.selectUser(user)}
>
{user.first} {user.last}
</li>
);
});
}
render() {
return (
<ul>
{this.renderList()}
</ul>
);
}
}
// Get apps state and pass it as props to UserList
// > whenever state changes, the UserList will automatically re-render
function mapStateToProps(state) {
return {
users: state.users
};
}
// Get actions and pass them as props to to UserList
// > now UserList has this.props.selectUser
function matchDispatchToProps(dispatch){
return bindActionCreators({selectUser: selectUser}, dispatch);
}
// We don't want to return the plain UserList (component) anymore, we want to return the smart Container
// > UserList is now aware of state and actions
export default connect(mapStateToProps, matchDispatchToProps)(UserList);
https://github.com/buckyroberts/React-Redux-Boilerplate
Or
import React from "react"
import { connect } from "react-redux"
import { fetchUser } from "../actions/userActions"
import { fetchTweets } from "../actions/tweetsActions"
#connect((store) => {
return {
user: store.user.user,
userFetched: store.user.fetched,
tweets: store.tweets.tweets,
};
})
export default class Layout extends React.Component {
componentWillMount() {
this.props.dispatch(fetchUser())
}
fetchTweets() {
this.props.dispatch(fetchTweets())
}
render() {
const { user, tweets } = this.props;
if (!tweets.length) {
return <button onClick={this.fetchTweets.bind(this)}>load tweets</button>
}
const mappedTweets = tweets.map(tweet => <li>{tweet.text}</li>)
return <div>
<h1>{user.name}</h1>
<ul>{mappedTweets}</ul>
</div>
}
}
https://github.com/learncodeacademy/react-js-tutorials/tree/master/5-redux-react
The first way uses 2 different functions mapStateToProps() and matchDispatchToProps() while the other way uses #connect(....).
When I use the #connect I get a whole bunch of warnings saying that it has not been finalized and might change.
The # symbol is a decorator which is still considered experimental. So I would use that at your own risk. Your first code block is the safer way to do it as described in the official docs. Both blocks essentially do the same thing but decorators are more sugar than anything.
References:
https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options
What's the '#' (at symbol) in the Redux #connect decorator?
I think the first method will give you less problems in the end. Someone else can chime in though too.
The answer by Jackson is right in every sense however he is missing out the importance of using the first version for the usage of unit testing. If you want to be able to unit test a component (which usually means testing with the unconnected version) you need to be able to export the connected and unconnected component.
Using your example and assuming you are using jest/enzyme you could do something like this:
// notice importing the disconnected component
import { UserList } from '../relative/file/path/UserList'
import { mount } from 'enzyme'
describe('UserList', () => {
it('displays the Username', () => {
const users = [{fist: 'Person', last: 'Thing'}, ... ]
const UserList = mount(<UserList users={users} />)
export(UserList.find('li')[0].text()).toEqual('Person Thing')
});
});
Once you build larger projects being able to unit test will provide sanity to your coding life. Hope this helps