this.props.dispatch is not a function in react js component file - reactjs

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);

Related

Redux error: mapDispatchToProps throws error

I am new to react-redux and I am having some difficulty in understanding the syntax. I am pasting my sample code below... please help me understand if there are any syntactical errors.
SampleParent.js:
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { fetchNames, fetchDownloadLink } from '../../actions/actions'
import SampleChild from '../ui/SampleChild'
class SampleParent extends Component {
constructor(props) {
super(props) ;
}
componentDidMount() {
const { dispatch } = this.props
dispatch(fetchNames());
}
render() {
return(<div><ul id="myUL">{this.props.reports.map((report) => (
<li>
<SampleChild
key={report.id}
label={report.label}
uri={() => fetchDownloadLink("http://localhost:8080/sample"+this.props.uri+".pdf")}
/>
</li>))}</ul></div>)}
}
function mapStateToProps(state) {
const { reports } = state
return {
reports
}
}
const mapDispatchToProps = dispatch => {
return {
fetchDownloadLink(url) {
dispatch(
fetchDownloadLink(url)
)
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ReportsApp)
SampleChild.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { fetchDownloadLink } from '../../actions/actions'
class OpenReport extends Component {
constructor(props) {
super(props) ;
}
render(){
return(<div className="in_sample" id={this.props.label}>
{this.props.label}
<a href={this.props.uri}>
<img src="../images/pdf-file_128.png" height="25px" width="25px"></img></a><br></br></div>
)
}
}
module.exports = OpenReport;
Currently I am getting this error:
Uncaught TypeError: dispatch is not a function
at ReportsApp.componentDidMount (bundle.js:39883)
Basically what I need to do is get a url as a string from the 'fetchDownloadLink ' function and pass this string to my child component. Is there any other way to do that?
Please suggest...
Thanks in advance!
According to the Documentaion:
mapDispatchToProps returns an object that somehow uses dispatch to bind
action creators in your own way.
However in your case you are returning an object without keys. Change your function to
const mapDispatchToProps = dispatch => {
return {
fetchDownloadLink: (url) => dispatch(fetchDownloadLink(url))
}
}
}
MoreOver, using connect function you need to connect mapStateToProps and mapDispatchToProps function to the component in which you will be using the action creators which in your case is SampleParent
Also if you pass mapDispatchToProps as the second parameter to connect, then dispatch is not available as a prop to your component.
So change your code to the following
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { fetchNames, fetchDownloadLink } from '../../actions/actions'
import SampleChild from '../ui/SampleChild'
class SampleParent extends Component {
constructor(props) {
super(props) ;
}
componentDidMount() {
const { dispatch } = this.props
this.props.fetchNames();
}
render() {
return(<div><ul id="myUL">{this.props.reports.map((report) => (
<li>
<SampleChild
key={report.id}
label={report.label}
uri={() => this.props.fetchDownloadLink("http://localhost:8080/sample"+this.props.uri+".pdf")}
/>
</li>))}</ul></div>)}
}
}
function mapStateToProps(state) {
const { reports } = state
return {
reports
}
}
const mapDispatchToProps = dispatch => {
return {
fetchDownloadLink: (url) => dispatch(fetchDownloadLink(url)),
fetchNames: () => dispatch(fetchNames)
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(SampleParent)
The problem is that you are passing your action creator as a prop uri to your child component, and then using that prop as an HREF tag.
You should instead pass it as onChildClick (example name) prop to your report item component, and call it on the onClick prop of the <a>.
<a onClick={ this.props.onChildClick }>xxxx</a>
The previous answer about the action creator mapping is fine, but you don't even need a function: if you use an object with functions as keys, they will be wrapped with dispatch for you.
const mapDispatchToProps = {
fetchDownloadLink
}
EDIT after your updated question
I see the problem now. You don't have dispatch as prop because you're using mapDispatchToProps to provide some action creators as props. It doesn't make sense to map dispatch to fetchDownloadLink and not doing it as well for fetchNames. Map both or neither, but you shouldn't mix and match.

Cannot read property 'query' of null in react js

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,
}
}

react-async-poll with a connected component

Looking at the docs for react-async-poll I'm following the Usage example to integrate asyncPoll into my component, but I'm getting a Uncaught TypeError: dispatch is not a function complaint from within my onPollinterval function
import React, { Component } from 'react';
import { connect } from 'react-redux';
import asyncPoll from 'react-async-poll';
import { fetchCaCities, } from '../actions';
import MyMap from './my-map';
class CaliforniaMap extends Component {
componentDidMount() {
this.props.fetchCaCities();
}
render() {
return (
<div>
<h1>California Map</h1>
<MyMap center={[37.5, -120]} zoom={6} layers={[this.props.caCities]} />
</div>
);
}
}
const onPollInterval = (props, dispatch) => {
console.log(dispatch); // undefined
return dispatch(fetchCaCities());
};
const mapStateToProps = state => ({
caCities: state.map.california.caCities,
});
export default asyncPoll(60 * 1000, onPollInterval)(connect(
mapStateToProps, { fetchCaCities }
)(CaliforniaMap)
Maybe react-async-poll doesn't work for connected components?
According to the docs:
The dispatch parameter is only passed to [onInterval] if it is
available in props, otherwise it will be undefined.
The example they give is confusing because it does not define dispatch anywhere, but they show onPollInterval using it.

How to wrap multi actionCreators into one props?

I'm getting the following error:
Uncaught TypeError: this.props.dispatch is not a function
Here's my component:
import React from 'react';
import PropTypes from 'prop-types';
import {Link} from 'react-router-dom';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import * as jobTitleSkillsActions from '../../actions/jobTitleSkillsActions';
import SkillList from './SkillList';
import * as userPositionActions from '../../actions/userPositionActions';
class SkillPage extends React.Component {
componentDidMount() {
this.props.dispatch(userPositionActions.loadUserPositions());
var job_title_id = this.props.user_positions[0].job_title_id; this.props.dispatch(jobTitleSkillsActions.loadJobTitleSkills(job_title_id));
}
.....
const mapStateToProps = state => {
return {
job_title_skills: state.job_title_skills,
user_positions: state.user_positions
};
};
function mapDispatchToProps(dispatch) {
return {
actions: {
userPositionActions: bindActionCreators(userPositionActions, dispatch),
jobTitleSkillsActions: bindActionCreators(jobTitleSkillsActions, dispatch),
}
};
}
export default connect(mapStateToProps, mapDispatchToProps)(SkillPage);
What am I doing wrong here?
Per my comment, this would be the correct syntax to bind multiple sub-objects worth of action creators:
function mapDispatchToProps(dispatch) {
return {
userPositionActions : bindActionCreators(userPositionActions, dispatch),
jobTitleSkillsActions: bindActionCreators(jobTitleSkillsActions, dispatch),
}
}
Since you have already used mapDispatchToProps, dispatch wont be available to the component as a prop. Since you have used mapDisptachToProps, the jobs actions will be available as props and you can use them like
componentDidMount() {
this.props.actions.userPositionActions.loadUserPositions());
var job_title_id = this.props.user_positions[0].job_title_id;
this.props.actions.jobTitleSkillsActions.loadJobTitleSkills(job_title_id));
}
However you can simplify it further like
function mapDispatchToProps(dispatch) {
return bindActionCreators({userPositionActions, jobTitleSkillsActions}, dispatch)
}
...
componentDidMount() {
this.props.userPositionActions.loadUserPositions());
var job_title_id = this.props.user_positions[0].job_title_id;
this.props.jobTitleSkillsActions.loadJobTitleSkills(job_title_id));
}
As you have provided mapDispatchToProps to connect function, dispatch is not passed as prop to your component.
Your componentDidMount code should be like this:
componentDidMount() {
const actions = this.props.actions
actions.userPositionActions.loadUserPositions()
var job_title_id = this.props.user_positions[0].job_title_id;
actions.jobTitleSkillsActions.loadJobTitleSkills(job_title_id)
}

How do you update a component in React when new data arrives on a stream?

I'm using the electron-boilerplate and Kurt Weiberth's tutorials to create my first node.js native app. I was able to create the app in the tutorial and now I want to add a component that gets updated when new tweets are streamed in given a query.
To do this, I created Tweet, TweetStream, and TweetFeed components, below. This kind of works, but I keep getting an error
Warning: flattenChildren(...): Encountered two children with the same key, ###############. Child keys must be unique; when two children share a key, only the first child will be used.
There are no duplicates when I look at the state for tweets, so I'm not sure why React is encountering them. Have I put something in the wrong place? Putting the Twit stream in a Component doesn't feel right, but I'm not sure where else it could go. I'd like to be able to update the query at some point so it seems like it needs to respond to an event when the query is updated.
Tweet
import React, { Component } from 'react';
class Tweet extends Component {
render() {
return (<li>
{this.props.tweet}
</li>);
}
}
export default Tweet;
TweetStream
import React, { Component } from 'react';
import Tweet from './Tweet';
class TweetStream extends Component {
render() {
return (
<ul>
{
this.props.tweets.map((tweet) => {
return <Tweet key={tweet.id} tweet={tweet.text} />;
})
}
</ul>
);
}
}
export default TweetStream;
TweetFeed
import React, { Component } from 'react';
const express = require('express');
const Twit = require('twit');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io')(server);
class TweetFeed extends Component {
handleTweet(tweet) {
this.state = {
id: tweet.id,
text: tweet.text
};
this.props.actions.addTweet(tweet);
}
render() {
const ts = this;
io.on('connection', function (socket) {
console.log('User connected. Socket id %s', socket.id);
socket.on('disconnect', function () {
console.log('User disconnected. %s. Socket id %s', socket.id);
});
});
const T = new Twit({
consumer_key: 'KEY',
consumer_secret: 'SECRET',
access_token: 'TOKEN',
access_token_secret: 'TOKEN_SECRET',
timeout_ms: 60 * 1000, // optional HTTP request timeout to apply to all requests.
});
const stream = T.stream('statuses/filter', { track: this.props.query });
stream.on('tweet', function (tweet) {
io.sockets.emit('tweet', tweet);
ts.handleTweet(tweet);
});
return (<div />);
}
}
export default TweetFeed;
Tweets Reducer
const initialTwitterState = [];
export default function reducer(state = initialTwitterState, action) {
switch (action.type) {
case 'ADD_TWEET':
return [{id: action.text.id, text: action.text.text}, ...state];
default:
return state;
}
}
These are called from a Home component
// #flow
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import React, {Component} from 'react';
import styles from './Home.css';
import TodoInput from './TodoInput';
import TweetStream from './TweetStream'
import TweetFeed from './TweetFeed'
import * as TodoActions from '../actions/todo';
import * as TwitterActions from '../actions/twitter';
class Home extends Component {
render() {
console.log(this.props)
return (
<div>
<TweetStream tweets={this.props.tweets} actions={this.props.tweet_actions}/>
<TweetFeed query={this.props.todos.query} tweets={this.props.tweets} todos={this.props.todos} actions={this.props.tweet_actions}/>
</div>
);
}
}
function mapStateToProps(state) {
return state;
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(TodoActions, dispatch),
tweet_actions: bindActionCreators(TwitterActions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Home);

Resources