Why are the props not injected into component? - reactjs

react/redux newbie here. Trying to build a todo app and have a container component like this:
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import Input from '../components/InputText'
import addTodo from '../actions/addTodoAction'
export class InputText extends Component {
static propTypes = {
prop: PropTypes
}
}
export default connect(null, {addTodo})(Input)
The 'stateless' component Input looks like this:
import React from 'react'
import PropTypes from 'prop-types'
const InputText = ({addTodo}) => {
return (
<div>
<input type='text'></input>
<button onClick={this.props.addTodo}>add todo</button>
</div>
)
}
//question :define proptype for action?
InputText.propTypes = {
addTodo: PropTypes.func.isRequired
}
export default InputText
This is the actioncreator:
const addTodo = (param) => ({
type: 'ADD_TODO',
payload: param.text
})
export default addTodo;
When I run the app I get this error:
TypeError: Cannot read property 'addTodo' of undefined
So looks like the props are not passed in. What am I doing wrong? Link to
githubrepo

You don't have to use this.props.addTodo, just use it simply <button onClick={addTodo}>add todo</button>.
You have directly taken addTodo by destructuring the props.

Related

Failed prop type: Invalid prop of type `function` supplied expected a single ReactElement

I'm trying to make a post using react v4 and axios, I was following this tutorial and I did exactly as he said, but I'm getting an error:
Maybe because I'm using another version of react, how can fix it?
Failed prop type: Invalid prop userSignupRequest of type function
supplied to SignUp, expected a single ReactElement.
here is my code:
import React from 'react';
import SignUpForm from './SignUpForm';
import { connect } from 'react-redux';
import { userSignupRequest } from '../../actions/signupActions';
import PropTypes from 'prop-types';
class SignUp extends React.Component{
render(){
const { userSignupRequest } = this.props;
return(
<SignUpForm userSignupRequest={userSignupRequest}/>
);
}
}
SignUp.propTypes = {
userSignupRequest: PropTypes.element.isRequired
}
export default connect(null, { userSignupRequest })(SignUp);
SignUp Actions:
import axios from 'axios';
import Constants from '../components/Constants'
export function userSignupRequest(userData) {
return dispatch => {
return axios.post(Constants.URL_REGISTER, userData);
}
}
Did you mean to have have element instead of func in your PropTypes?
SignUp.propTypes = {
userSignupRequest: PropTypes.func.isRequired
}

Getting warning when trying to use router.history.goBack with react-router

I getting this warning:
Warning: BackButton: type specification of context router is invalid; the type checker function must return null or an Error but returned a boolean. You may have forgotten to pass an argument to the type checker creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and shape all require an argument).
Here is my code (its a BackButton component I want to reuse)
import React, { Component } from "react";
import { Button } from 'antd';
class BackButton extends Component {
static contextTypes = {
router: () => true,
}
render() {
return (
<Button
onClick={this.context.router.history.goBack}>
Back
</Button>
)
}
}
export default BackButton;
I would preferable use PropTypes instead if possible, but I do not know how...
Your example with propTypes:
import React, { Component } from "react";
import { Button } from 'antd';
import PropTypes from 'prop-types'; // You need to add this dependency
class BackButton extends Component {
static contextTypes = {
router: PropTypes.object
}
render() {
return (
<Button
onClick={this.context.router.history.goBack}>
Back
</Button>
)
}
}
export default BackButton;
You can read more about propTypes here: https://reactjs.org/docs/typechecking-with-proptypes.html.
The contextTypes for router need to be defined like PropTypes.object.isRequired, install prop-types from npm first using
npm install -S prop-types
and import it like
import PropTypes from 'prop-types'
and define context as
static contextTypes = {
router: PropTypes.object.isRequired
}
so you code will look like
import PropTypes from 'prop-types'
class BackButton extends Component {
static contextTypes = {
router: PropTypes.object.isRequired
}
render() {
return (
<Button
onClick={this.context.router.history.goBack}>
Back
</Button>
)
}
}

WebStorm highlights React component when props passed through react-redux connect function

I have a container component:
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
class MyComponent extends Component {
static propTypes = {
someEntities: PropTypes.object.isRequired
}
....
}
export default connect(state => ({ someEntities: state.someEntities })(MyComponent)
So I am passing props via connect to this component, but when I place component in code, like:
....
import MyComponent from './MyComponent';
....
<div><MyComponent /></div>
....
WebStorm highlights MyComponent and gives me an error: Element MyComponent doesn't have required attribute someEntities.
I am using version 2016.3.1. Is this an error? Should I use propTypes in this case?
This is a known issue, tracked as WEB-21692; please follow it for updates (https://intellij-support.jetbrains.com/hc/en-us/articles/207241135-How-to-follow-YouTrack-issues-and-receive-notifications)

Reusable components Redux

I have a reusable component having its own action and reducer that i then use in another component.
Component AddToCart
import React, { Component } from 'react'
import { bindActionCreators } from 'redux'
import Button from 'environment/atoms/button'
import * as AppIndexActionsCreators from 'environment/AppIndexActionsCreators'
const AddToCart = (props)=>{
let boundActionCreators = bindActionCreators(AppIndexActionsCreators)
return(
<Button
txt="Add To Cart"
{...boundActionCreators}
/>
)
}
export default AddToCart;
I pass it in
import React from 'react'
import { Link } from 'react-router'
import ProductDesc from '../Molecules/ProductDesc'
import ProductImg from 'environment/sharedMolecules/ProductImg'
import AddToCart from 'environment/sharedMolecules/AddToCart'
const Product = (props) => {
const product = props.product;
return (
<div>
<Link to={`/productDesc/${product.id}`}>
<ProductDesc {...props} />
<ProductImg {...props}
size="small"
/>
</Link>
<AddToCart/>
</div>
)
}
Product.propTypes = {
displayProduct: React.PropTypes.func,
product: React.PropTypes.object
};
On Click on AddToCart nothing happens where it should print a console.log as defined in my Reducer...when inspecting the AddToStore component in the browser i can see in the props that the component sees the AddToCart fn defined in the Action file......
looks like Action is not dispatched to the reducer...how to fix this ?
Use redux connect decorator.
Firstly, import it from react-redux bindings:
import { connect } from 'react-redux';
Then decorate your component with it!:
connect(mapStateToProps, mapDispatchToProps)(AddToCart)
Where functions in arguments should be defined somewhere like:
function mapStateToProps(state) {
return {
// someStoreVar: state.someStore.someStoreVar
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ ...AppIndexActionsCreators }, dispatch);
}
First one is to pass store state to props, second one is to pass
actions to props. They are fully optional and if you don't need the
store nor the actions bound to props you can omit them with null
like:
connect(null, mapDispatchToProps)(AddToCart)
Finally you will wan't this decorated component to be exported by
default instead of not-decorated one:
export default connect(mapStateToProps, null)(AddToCart)
At this moment you will be able to dispatch any actions or to access any store vars from props inside the component. This is a default technique to do this in react and you will use this quite alot. If this still sounds confusing feel free to ask for explanations.
I ended up rewriting my component as suggested by Syberic
import React, { Component } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux';
import Button from 'environment/atoms/button'
import * as AppIndexActionsCreators from 'environment/AppIndexActionsCreators'
const AddToCart = (props) => {
// let boundActionCreators = bindActionCreators(AppIndexActionsCreators)
return(
<Button
txt="Ajouter au panier"
clickAddToCart = { props.addToCart }
/>
)
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(AppIndexActionsCreators, dispatch);
}
export default connect(null,mapDispatchToProps)(AddToCart)
Additionally i corrected my Reducer where there was a syntax error. I did not put 'export default' but only 'export'
import { ADD_TO_CART } from './ActionTypes'
export default function cart(state=[],action){
console.log();
switch (action.type){
case ADD_TO_CART:
console.log('yeah');
return state;
default:
return state;
}
}

React Test: Invariant Violation: Element type is invalid: undefined

Hello I'm learning React/redux
I created a todo app it works fine, but when I try to write a component test with jsdom it shows me this error, spent some time but could not figured out the problem:
the component (it's just a textfield + button)
import React from 'react'
import {addTodo} from '../actions'
import {connect} from 'react-redux'
let inputText
class AddTodo extends React.Component {
constructor(props, context){
super(props, context)
}
render(){
const {text, handleAdd} = this.props
return (
<div>
Text:
<input type='text' ref={node=>{ inputText=node}} /> <button onClick={()=>handleAdd(inputText.value)} >Add </button>
</div>
)
}
}
const handleAdd = (text)=>{
dispatch(addTodo(text))
}
const mapDispatchToProps = (dispatch, ownProps) =>{
return {
handleAdd: (text)=>{
dispatch(addTodo(text))
}
}
}
AddTodo = connect(null,mapDispatchToProps)(AddTodo)
export default AddTodo
The spec.js
import React from 'react'
import ReactDOM from 'react-dom'
import {
renderIntoDocument,
scryRenderDOMComponentsWithTag,
Simulate
} from 'react-addons-test-utils'
import {AddTodo} from '../../src/containers/AddTodo'
import {expect} from 'chai'
describe('Add todo', ()=>{
it('render the button and text field', ()=>{
const component = renderIntoDocument(<AddTodo />);
const button = scryRenderDOMComponentsWithTag(component, 'button');
const input = scryRenderDOMComponentsWithTag(component, 'input');
expect(button.length).to.be(1);
expect(input.length).to.be(1);
})
})
The error:
Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined.
You are exporting component as default export:
export default AddTodo
But importing it as named export
import {AddTodo} from '../../src/containers/AddTodo'
So AddTodo is undefined.
Change your import to:
import AddTodo from '../../src/containers/AddTodo'

Resources