I have a react component that will throws an error when I try to dispatch a redux action. Here is the component:
import React from 'react';
import { connect } from 'react-redux';
import { createBook } from './actions/books';
import './InputForm.css';
export class InputForm extends React.Component {
onFormSubmit(event) {
event.preventDefault();
let newBook = {
title: this.titleInput.value,
author: this.authorInput.value,
description: this.descriptionInput.value
}
this.props.dispatch(createBook(newBook));
}
render() {
return (
<div>
<form onSubmit={event => this.onFormSubmit(event)}>
<input className="form-input" placeholder="Title" ref={input => this.titleInput = input} />
<input className="form-input" placeholder="Author" ref={input => this.authorInput = input} />
<input className="form-input form-text-area" placeholder="Description" ref={input => this.descriptionInput = input} />
<button className="form-button" type="submit">Submit</button>
</form>
</div>
)
}
}
export default connect()(InputForm);
This is the line that creates the problem:
this.props.dispatch(createBook(newBook));
I'm not sure what's going on. Here is the error I get:
Uncaught TypeError: this.props.dispatch is not a function
at InputForm.onFormSubmit (InputForm.js:15)
at onSubmit (InputForm.js:21)
at HTMLUnknownElement.callCallback
I have another component set up almost identically using a different action. It works fine. Here is that component if anyone wants to compare:
import React from 'react';
import './Book.css';
import { deleteBook } from './actions/books';
import { connect } from 'react-redux';
export class Book extends React.Component {
onDelete(event) {
this.props.dispatch(deleteBook(this.props.id));
}
render() {
return (
<div className="book-container">
<h4>{this.props.title}</h4>
<p>{this.props.author}</p>
<p>{this.props.description}</p>
<p>{this.props.id}</p>
<button className="book-delete-button" onClick={event => this.onDelete(event)}>Delete</button>
</div>
)
}
}
export default connect()(Book);
Dunno what's going on here. Please help!
Seems like there at least 2 issues here that can cause a problem.
It depends on how you import the components. You are exporting 2
components in each module:
A named export (the component)
And a default export (the new connected component).
If you import the named export:
import { InputForm } from './path';
You will not have the redux related props as this is not the
connected component.
This is why one of your other connected components is working fine even though it seems to have the same code and structure pattern.
You probably import it the way you should (default import).
Make sure you import the default component:
import InputForm from './path';
Another thing that caught my eye, is that you are not binding the
event handler onFormSubmit, so i expect the this to be the
element that triggered the event. Though i think if that was the
issue, you would get a different error anyway.
I had imported the disconnected version in the container component. Stupid mistake.
Related
I am using react along with redux and material-ui to make a component. I am attempting to write an export statement export default connect()(withRouter(FirstPage))(withStyles(styles)(FirstPage))
However, this doesn't seem to work I get an error that says
TypeError: Cannot set property 'props' of undefined
this.props = props;
This error is referencing one of my node_modules.
Here is my full code:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import {withRouter} from 'react-router-dom'
import { withStyles } from '#material-ui/core/styles';
import Card from '#material-ui/core/Card';
import CardActions from '#material-ui/core/CardActions';
import CardContent from '#material-ui/core/CardContent';
import Button from '#material-ui/core/Button';
const styles = theme =>({
root: {
maxWidth: 345,
},
})
class FirstPage extends Component {
state = {
feeling: ''
}
//This function will dispatch the users response to index.js
//The dispatch type here is 'SET_FEELING'
submitData=(event) => {
event.preventDefault();
this.props.dispatch({type: 'SET_FEELING', payload: this.state})
this.changeLocation();
}
//This function will update the local state with the users response
handleChange= (event) => {
this.setState({
feeling: event.target.value
})
}
//This function will change the current url when a button is clicked
changeLocation= ()=> {
this.props.history.push('/secondPage')
}
render(){
const { classes } = this.props;
return(
<div>
<Card >
<CardContent className={classes.root}>
<form>
<input onChange={this.handleChange} placeholder='How are you feeling' value={this.state.feeling} />
</form>
</CardContent>
<CardActions>
<Button onClick={this.submitData}>Submit</Button>
</CardActions>
</Card>
</div>
)
}
}
//this export connects the component to the reduxStore as well as allowing us to use the history props
export default connect()(withRouter(FirstPage))(withStyles(styles)(FirstPage))
I believe the following code should work:
export default withRouter(connect()(withStyles(styles)(FirstPage)))
Instead of
export default connect()(withRouter(FirstPage))(withStyles(styles)(FirstPage))
First of all, connect() returns a function that only accepts an argument. Second, connect() should be wrapped inside withRouter(). This problem is stated in the github docs of React Router.
without using react-redux :
export default (withStyles(styles), withRouter)(FirstPage);
I have two components which both use the connect HOC.
import {connect} from "react-redux";
import ComponentB from "./ComponentB";
class ComponentA extends Component {
render(){
return {
<div>
<button
onClick={this.refs.ComponentB.showAlert()}
>
Button
</button>
<ComponentB
ref={instance => {
this.ComponentB = instance.getWrappedInstance();
}}
/>
</div>
}
}
}
export default connect(mapStateToProps, {}, null, {withRef: true})(ComponentA)
Having ComponantA with the connect HOC gives me the error "TypeError: Cannot read property 'getWrappedInstance' of null"
export default ComponantA;
Not using the HOC would not give me this error.
import {connect} from "react-redux";
class ComponentB extends Component {
showAlert = () => {
alert("Please Work");
}
render(){
return {
<div>ComponentB</div>
}
}
}
export default connect(mapStateToProps, {}, null, {withRef: true})(ComponentB)
React.createRef was introduced in React 16.3 and is supposed to be used like:
this.componentBRef = React.createRef();
...
<button
onClick={() => this.componentBRef.current.getWrappedInstance().showAlert()}
>
Button
</button>
<ComponentB
ref={this.componentBRef};
}}
/>
As explained in this answer, the pattern used in createRef allows to lazily access a ref through current property because this.componentBRef.current is initially null.
Since Redux is in use, there's a chance that the interaction between components should be performed via Redux instead.
I'm getting this error when web pack compiles:
I couldn't find an answer on google or Stackoverflow.
Here is my component:
import React from 'react'
import { Form, Button } from 'react-bootstrap'
import { reduxForm } from 'redux-form'
import { connect } from 'react-redux'
import ContactInfo from './ContactInfo'
const CheckoutForm = props => {
return (
<Form className="forms" onSubmit={props.handleSubmit}>
<PanelGroup id="checkout-panels">
<ContactInfo {...props} />
</PanelGroup>
<Button className="btn-green pull-right" type="submit" >Submit</Button>
</Form>
)
}
CheckoutForm = reduxForm({
form: 'checkoutForm',
destroyOnUnmount: true
})(CheckoutForm)
CheckoutForm = connect(state => ({
initialValues:
{
payment_type: 'credit_card',
...state.checkout
}
}))(CheckoutForm)
export default CheckoutForm
Can anyone help? I'm sure its a super simple fix. I just can't find it. Thank you!
Your problem is here
const CheckoutForm
Change it for
let CheckoutForm
Consts are read-only, you just can set a value one time, and after that you can't change it.
Read here about consts Const in ES6
Ah! Found it. It was a simple oversight. Hope this helps someone.
I needed to change const to let:
let CheckoutForm = props => {
return (
...
)
}
I using Redux-Form 7.3.0. I am trying to get the values of my form in another component. I read the instruction at the website of Redux form but didn't work.
this is the code of my componenet:
import React from 'react'
import { Field, reduxForm } from 'redux-form';
import { connect } from 'react-redux';
import { formValueSelector } from 'redux-form';
class Test extends React.Component {
render() {
console.log(this.props);
return (
<div>
test
{this.props.title}
</div>
);
}
}
const selector = formValueSelector('NewPostForm');
Test = connect(
state => ({
title: selector(state, 'title')
})
)(Test)
export default Test;
This is my form component:
import React from 'react';
import { Field, reduxForm } from 'redux-form';
class NewPost extends React.Component {
renderField(field) {
return (
<div>
<label>{field.label}</label>
<input type="text" {...field.input} />
</div>
);
}
showResults(values) {
window.alert(`You submitted:\n\n${JSON.stringify(values, null, 2)}`);
}
render() {
const { pristine, submitting, handleSubmit } = this.props;
return (
<form onSubmit={handleSubmit(this.showResults)} >
<div>
<Field
label='Title'
name='title'
component={this.renderField}
/>
<button type='submit' disabled={submitting}>
Submit the from :)
</button>
</div>
</form>
);
}
}
export default reduxForm({ form: 'NewPostForm'})(NewPost);
but I always get
title:undefined
I found the same question here but it did not help me.
Your Test component has two imports from "redux-form". Please make it just one, like this:
import { Field, reduxForm, formValueSelector } from 'redux-form'
If your NewPost component gets unmounted at any moment, maybe by changing view or something during a navigation, the state of the form gets destroyed. You can avoid such default behavior by adding destroyOnUnmount attribute with a false value to your reduxForm wrapper:
export default reduxForm({
form: 'NewPostForm',
destroyOnUnmount: false
})(NewPost)
If this doesn't help you, please provide a better context of how you're using your components.
UPDATE: I made an example where I define 4 components:
NewPost.js: It's the form connected to the store with redux-form.
ShowPost.js: Shows what was captured (the post) by the form when you hit the submit button. This data is set to the NewPost internal state, and then it's passed as prop.
ShowPostFromSelector.js: Shows what is being captured by the form, this due to the use of the selector formValueSelector.
App.js: It's the container of the 3 components above, where the onSubmit function is defined.
Here it is: https://codesandbox.io/s/w06kn56wqk
I am receiving this error: this.props.postBooks is not a function.
I have an action - postBooks - which I am trying to dispatch via props.
Here is my component:
"use strict"
import React from 'react'
import {Well,Panel,FormControl,FormGroup,ControlLabel,Button} from 'react-bootstrap'
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux'
import {postBooks} from '../../actions/booksActions'
import {findDOMNode} from 'react-dom'
export class BooksForm extends React.Component{
handleSubmit(){
const book = [{
title: findDOMNode(this.refs.title).value,
description: findDOMNode(this.refs.description).value,
price: findDOMNode(this.refs.price).value
}]
this.props.postBooks(book)
}
render(){
return(
<Well>
<Panel>
<FormGroup controlId='title'>
<ControlLabel> Title </ControlLabel>
<FormControl
type='text'
placeholder='Enter Title'
ref='title' />
</FormGroup>
<FormGroup controlId='description'>
<ControlLabel> Enter Description </ControlLabel>
<FormControl
type='text'
placeholder='Enter Description'
ref='description' />
</FormGroup>
<FormGroup controlId='price'>
<ControlLabel> Enter Price </ControlLabel>
<FormControl
type='text'
placeholder='Enter Price'
ref='price' />
</FormGroup>
<Button
onClick={this.handleSubmit.bind(this)}
bsStyle='primary'> Enter New Book </Button>
</Panel>
</Well>
)
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators({postBooks},dispatch)
}
export default connect(null,mapDispatchToProps)(BooksForm);
It seems that dispatch is not being mapped to props as expected since upon console logging props, props are empty. Any help appreciated. Thanks in advance
Edit: Added Actions
"use strict"
// POST A BOOK
export function postBooks(book){
return {
type:"POST_BOOK",
payload: book
}
}
// DELETE A BOOK
export function deleteBooks(id){
return {
type:"DELETE_BOOK",
payload: id
}
}
//UPDATE BOOK
export function updateBooks(book){
return {
type:"UPDATE_BOOK",
payload: book
}
}
//Retrieve all books as if using API
export function getBooks(){
return{
type:'GET_BOOKS'
}
}
Figured it out.
So I was exporting component up top and export default below.
Noticed webpack was giving an error 'import and export may only appear at top level'. Went ahead and removed top export and now works as expected.
Faced the same issue and I just removed the curly braces from import:
From
import {AppTest} from "./AppTest";
To
import AppTest from "./AppTest";
And that seemed to fixed the issue. Note that I had "export default" in my class already.
So I had this same issue & the problem was the same as shown in the other useful answers but I will try to explain why this happens in more details
If we define the class with a default export & a named export as shown here (based on the question):
// named export
export class BooksForm extends React.Component{
.......
function mapDispatchToProps(dispatch){
return bindActionCreators({postBooks},dispatch)
}
// default export
export default connect(null,mapDispatchToProps)(BooksForm);
Then we have to be careful when importing the class to import the default export not the named export:
This imports the default export & shall work correctly with redux
import BooksForm from "./thePath"
This imports the named export & won't work with redux
import { BooksForm } from "./thePath"
If we define the two imports in the same class (which is usually done to make unit testing easier) :
Then we have to import the default export when rendering the component in order to use the redux connect() higher order function that we have exported.
Sometimes if you just depend on the editor autoimport, it will import the named export (which doesn't have the redux connect) & therefore will give that error
Bottom line
You don't have to remove any of the 2 exports, just import the default export (without braces) to use redux connect() correctly
I faced the same issue once, and I found that importing two files into each other causes some kind of infinite loop and it just breaks the process. I was using flow static type checker, and while I was importing my action into my component, I imported my component into my action file for a type checking, and this issue occured, so I removed importing my component to my action file, and the problem solved.
Please try again after importing the redux store. Let me know if that worked or not.And also using refs is not recommended. https://facebook.github.io/react/docs/refs-and-the-dom.html