React/Redux argument becomes undefined after passed to action - reactjs

I have a simple search bar that has an onChange method to search/filter data.
class ItemSearch extends React.Component {
constructor(){
super();
this.handleOnChange = this.handleOnChange.bind(this);
}
handleOnChange(e){
const searchTerm = this.search.value;
console.log(searchTerm);
this.props.fetchSearch(searchTerm);
}
render() {
return (
<div>
<form>
<input type='text' ref={(input) => {this.search = input}} onChange={this.handleOnChange}></input>
</form>
<div>
{this.props.data.map((item, i) => {
return <ListItem
key={i}
item={item}
/>
})}
</div>
</div>
)
}
}
Logging out the search term gives me the correct value. However, after it is passed to the redux action creator it becomes undefined. My action is pretty simple, something like this.
export default function fetchSearch(value){
console.log(value);
return {
type: 'FETCH_SEARCH',
payload: value
}
}
In this log it has become undefined. I assume it has something do to with it being out of scope, but without passing arguments to action creators, there is no way of lifting state up to the store from forms anyway. I have seen this exact thing working in many tutorials and posts so I am at a bit of a loss, although it is probably something silly I missed.
Thanks.

I just forgot to update the mapDipatchToProps in the container.
Once I did that it works fine. Thanks all!

Yes you have made a mistake here. Firstly you have to use redux form to get this thing done. There you have to connect this with the reducer or store like below.
import React, { Component } from 'react';
import { reduxForm } from 'redux-form';
import { createTimer } from '../actions/index';
class TimerNew extends Component {
onSubmit(props){
this.props.createTimer(props);
}
render() {
const { fields: { hours, minutes, seconds, label }, handleSubmit } = this.props;
return (
<form onSubmit={handleSubmit(this.props.createTimer)}>
<h5>Create A New Timer</h5>
<div className={`form-group ${hours.touched && hours.invalid ? 'has-danger' : ''}`}>
<label>Hours</label>
<input type="text" className="form-control" {...hours} />
<div className="text-help">
{hours.touched ? hours.error : ''}
</div>
</div>
<div className={`form-group ${minutes.touched && minutes.invalid ? 'has-danger' : ''}`}>
<label>Minutes</label>
<input type="text" className="form-control" {...minutes} />
<div className="text-help">
{minutes.touched ? minutes.error : ''}
</div>
</div>
<div className={`form-group ${seconds.touched && seconds.invalid ? 'has-danger' : ''}`}>
<label>Seconds</label>
<input type="text" className="form-control" {...seconds} />
<div className="text-help">
{seconds.touched ? seconds.error : ''}
</div>
</div>
<div className={`form-group ${label.touched && label.invalid ? 'has-danger' : ''}`}>
<label>Label</label>
<input type="text" className="form-control" {...label} />
<div className="text-help">
{label.touched ? label.error : ''}
</div>
</div>
<button type="submit" className="btn btn-primary">Submit</button>
</form>
);
}
}
function validate(values) {
const errors = {};
if (!values.hours) {
errors.title = 'Enter hours';
}
if (!values.minutes) {
errors.categories = 'Enter minutes';
}
if (!values.seconds) {
errors.content = 'Enter seconds';
}
if (!values.label) {
errors.content = 'Enter label';
}
return errors;
}
// connect: first argument is mapStateToProps, 2nd is mapDispatchToProps
// reduxForm: 1st is form config, 2nd is mapStateToProps, 3rd is mapDispatchToProps
export default reduxForm({
form: 'TimerNewForm',
fields: ['hours', 'minutes', 'seconds', 'label'],
validate
}, null, { createTimer })(TimerNew);
Here you have to wrap your form with the call to the reduxForm helper and pass in the action creator in my case it is createTimer. Also note that the code may differ based on the redux-forms version you are using. Here I am using 4.3.*. You may check out the redux-forms documentation here for further details. Hope this helps. Happy coding.

Related

TypeError: Cannot read property 'validateFields' of undefined reactjs & django? how can i resolve this issue without antd?

when i try to login then i get this error (TypeError: Cannot read property 'validateFields' of undefined), i also read the documentation about antd but i don't want to use antd in my project how can i resolve this issue without antd... i have simple react-bootstrap Form... anybody know how can i solve this issue?
login.js
import React from 'react'
import { Link } from 'react-router-dom'
import { connect } from 'react-redux';
import { Form } from 'react-bootstrap';
import * as actions from './actions/auth';
import FullPageLoader from "../components/FullPageLoader";
class NormalLoginForm extends React.Component {
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
this.props.onAuth(values.userName, values.password);
this.props.history.push('/');
}
});
}
render() {
let errorMessage = null;
if (this.props.error) {
errorMessage = (
<p>{this.props.error.message}</p>
);
}
return (
<div className="form-flex">
{errorMessage}
{
this.props.isLoading ?
<FullPageLoader />
:
<Form onSubmit={this.handleSubmit} className="login-form form-group">
<h1 className="h4 text-center font-weight-normal">Sign in</h1>
<div className="form-group">
<input
className="form-control"
name="username"
autoComplete="off"
type="text"
required
placeholder="username" />
</div>
<div className="form-group">
<input
className="form-control"
name="password"
autoComplete="off"
type="password"
required
placeholder="password" />
</div>
<div className="form-group">
<button type="submit" className="btn btn-block btn-secondary">Login</button>
</div>
<small>
<Link to="/passwordforget">Forgot password?</Link>
</small>
</Form>
}
</div >
);
}
}
const mapStateToProps = (state) => {
return {
loading: state.loading,
error: state.error
}
}
const mapDispatchToProps = dispatch => {
return {
onAuth: (username, password) => dispatch(actions.authLogin(username, password))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(NormalLoginForm);
The error Cannot read property 'validateFields' of undefined is being caused by the code this.props.form.validateFields. You are not passing a form prop into the component so it is undefined, and throwing that error.
Either define a validator somewhere and use it, or remove that line.

Setting the default value of an input field after data is retrieved causes the content to overlap and the "onChange" event not to be triggered

I have an "edit category" component in my React application.
The ID is passed through the URL.
When the component is mounted, the action "fetchCategory" is called, which updates the props on the component with the current category.
I have a form which I want to be pre-populated, which I'm currently doing using the defaultValue on the input.
However, this isn't reflected on the state and the label for the text field overlaps the input field.
Any help would be greatly appreciated. I'll leave snippets of my code below which could help with understanding what I'm trying to do.
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchCategory } from "../../store/actions/categoryActions";
class AddOrEditCategory extends Component {
componentDidMount() {
this.props.fetchCategory(this.props.match.params.id);
if (this.props.match.params.id) {
this.setState({
_id: this.props.match.params.id
});
}
}
handleSubmit = e => {
e.preventDefault();
console.log(this.state);
};
handleChange = e => {
this.setState({
[e.target.id]: e.target.value
});
};
render() {
const addingNew = this.props.match.params.id === undefined;
return (
<div className="container">
<h4>{addingNew ? "Add category" : "Edit category"}</h4>
<form onSubmit={this.handleSubmit}>
<div className="input-field">
<input
type="text"
id="name"
defaultValue={this.props.category.name}
onChange={this.handleChange}
/>
<label htmlFor="name">Category name</label>
</div>
<div className="input-field">
<input
type="text"
id="urlKey"
onChange={this.handleChange}
defaultValue={this.props.category.urlKey}
/>
<label htmlFor="urlKey">URL Key</label>
</div>
<button className="btn">{addingNew ? "Add" : "Save"}</button>
</form>
</div>
);
}
}
const mapStateToProps = state => {
return {
category: state.categoryReducer.category
};
};
export default connect(
mapStateToProps,
{ fetchCategory }
)(AddOrEditCategory);
EDIT: Included whole component as requested
You need to replace the 'defaultValue' attribute with 'value' in the inputs.
You are using a controlled vs uncontrolled component. You dont need to use defaultValue.
You can set the initial values on the promise success for fetchCategory
componentDidMount() {
this.props.fetchCategory(this.props.match.params.id).then(response => {
// Set the initial state here
}
}
OR in
componentWillReceiveProps(nextProps) {
// Compare current props with next props to see if there is a change
// in category data received from action fetchCategory and set the initial state
}
React docs
<form onSubmit={this.handleSubmit}>
<div className="input-field">
<input
type="text"
id="name"
onChange={this.handleChange}
value={this.state.name} //<---
/>
<label htmlFor="name">Category name</label>
</div>
<div className="input-field">
<input
type="text"
id="urlKey"
onChange={this.handleChange}
value={this.state.urlKey}
/>
<label htmlFor="urlKey">URL Key</label>
</div>
<button className="btn">{addingNew ? "Add" : "Save"}</button>
</form>

Getting undefined for Field name in reduxform

I have redux form in a class component and for some reason when I console log the formValues I am getting undefined what is the problem?
class CreateTeamBox extends Component{
handleFormSubmit({name}){
console.log(name);
}
renderError = ({ touched, error}) => {
if(touched && error) {
return(
<div>{error}</div>
);
}
}
renderInput = ({input, label, type, meta}) => {
return(
<div className={styles.formGroup}>
<label>{label}</label>
<input {...input} />
<div className={styles.errorWrapper}>
{this.renderError(meta)}
</div>
</div>
);
}
render() {
const {handleSubmit, error} = this.props ;
return(
<div className={styles.createTeamBox}>
<div className={styles.titleWrapper}>
<h2>create team</h2>
</div>
<div className={styles.bodyWrapper}>
<div className={styles.submitErrorWrapper}>
{error ? <Error error={error} /> : null}
</div>
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
<Field name="name" component={this.renderInput} label="name" />
<button className={styles.button} type="submit" >create</button>
</form>
</div>
</div>
)
}
}
const validate = (formValues) => {
console.log(formValues.name);
const name = formValues.name;
const errors = validateForm(name);
return errors;
}
export default reduxForm({
form: 'createTeamForm',
validate
})(CreateTeamBox);
I have other reduxform in the same given project with different names, is it causing the problem? Im not sure why it is happening as I havecopied most of the coe from working reduxform in the given project .
the default value of formValues will be empty object, after enter some data to the field then you can get the value, or you can pass the value by using initialValues

How can I get an input's value on a button click in a Stateless React Component?

I have the following functional component
const input = props => (
<div>
<input placeholder="Type a message..." />
<div onClick={props.send} className="icon">
<i className="fa fa-play" />
</div>
</div>
)
How could I possibly pass the value of the input to props.send()?
I found a solution for this exact scenario on React's official docs: https://reactjs.org/docs/refs-and-the-dom.html#refs-and-functional-components
This approach allows your component to remain stateless and also doesn't require you to update the parent component on every change.
Basically,
const input = props => {
let textInput = React.createRef();
function handleClick() {
console.log(textInput.current.value);
}
return (
<div>
<input ref={textInput} placeholder="Type a message..." />
<div onClick={handleClick} className="icon">
<i className="fa fa-play" />
</div>
</div>
)
}
Edit May 2021: Since this answer seems to be getting some attention, I have updated the answer to use a hooks based approach as well, since that is what I would use now (If using React 16.8 and above).
const input = props => {
const [textInput, setTextInput] = React.useState('');
const handleClick = () => {
console.log(textInput);
props.send(textInput);
}
const handleChange = (event) => {
setTextInput(event.target.value);
}
return (
<div>
<input onChange={handleChange} placeholder="Type a message..." />
<div onClick={handleClick} className="icon">
<i className="fa fa-play" />
</div>
</div>
)
}
There are many ways to do it since you're very much concerned about performance. Here is the implementation, your component will be rendered only when you click on send button which actually means state will be updated once and input value will be displayed in parent component.
const Input = props => {
return (
<div>
<input onChange={props.changeHandler} placeholder="Type a message..." />
<button onClick={props.send}>send</button>
</div>
);
};
class App extends Component {
state = {
inputValue: ""
};
inputValue = '';
send = () => {
this.setState({ inputValue: this.inputValue });
};
changeHandler = event => {
this.inputValue = event.target.value;
};
render() {
console.log("In render");
return (
<React.Fragment>
<Input changeHandler={this.changeHandler} send={this.send} />
<div> {this.state.inputValue}</div>
</React.Fragment>
);
}
}
Since you mentioned that you just started with React, I'd suggest that you work through the documentation (which offers nice explanation).
According to your comment, the usage of a functional component is not a requirement. Therefore I'd recommend to do it that way -->
Your CustomInput component:
import React from "react";
import PropTypes from "prop-types";
class CustomInput extends React.Component {
constructor() {
super();
this.textInput = React.createRef();
}
static propTypes = {
send: PropTypes.func
};
render() {
const { send } = this.props;
return (
<React.Fragment>
<input placeholder="Type a message..." ref={this.textInput} />
<div
onClick={() => send(this.textInput.current.value)}
className="icon"
>
CLICK ME
</div>
</React.Fragment>
);
}
}
export default CustomInput;
If you noticed, I've replaced the empty div with React.Fragment. In that case you can omit the unnecessary <div> wrappings (if those are not required) which will keep your DOM clean (Read more about it here.
Usage:
<CustomInput
send={(prop) => {
console.log(prop)
}}
/>
I just used a dummy function which will log the input value to the console..
You can check the working example (Make sure to trigger the console in the editor) here
Posting this answer, If incase someone is using an earlier release of React 16.3. We can achieve the same thing by using callback refs instead without having to maintain state or having onChange event handler:
const input = props => (
<div>
<input ref={props.myRef} placeholder="Type a message..." />
<div onClick={props.send} className="icon">
<i className="fa fa-play" />
</div>
</div>
)
Calling that Input Component
handleClick = () => console.log(this.inputRef.value);
<Input myRef={el => this.inputRef = el} send={this.handleClick} />

Cannot trigger component's function in my react app

I am currently working with react and I have runned into a problem this morning that i do not understand.
I am trying to handle a form submit from my component with a function by passing it in the onSubmit property but it does not trigger it. I then added a button to trigger a mock function with its onClick property, and i still got the same problem; it appears than I can't trigger my function and I cannot find any solution on the Google.
Here is my code so you can check it out:
import React from 'react';
import AgentsStore from '../stores/AgentsStore';
import AgentsActions from '../actions/AgentsActions';
class Agents extends React.Component {
constructor(props) {
super(props);
this.state = AgentsStore.getState();
this.onChange = this.onChange.bind(this);
}
componentDidMount() {
AgentsStore.listen(this.onChange);
}
componentWillUnmount() {
AgentsStore.unlisten(this.onChange);
}
onChange(state) {
this.setState(state);
}
handleSubmit(event) {
event.preventDefault();
var user = {};
user.name = this.state.newUser.name.trim();
user.lastname = this.state.newUser.lastname.trim();
if (user.name && user.lastname) {
AgentsActions.createUser(user);
}
}
onClick() {
console.log('clicked');
}
render() {
return (
<div /*className='container'*/>
<div className='alert alert-info'>
Hello from Agents
</div>
<div className='panel panel-default'>
<div className='panel-heading'>Create User</div>
<div className='panel-body'>
<form onSubmit={this.handleSubmit.bind(this)}>
<div className='form-group'>
<label className='control-label'>Name</label>
<input type='text'
className='form-control'
ref='nameTextField'
value={this.state.newUser.name}
onChange={AgentsActions.updateName} />
</div>
<div className='form-group'>
<label className='control-label'>Lastname</label>
<input type='text'
className='form-control'
ref='lastnameTextField'
value={this.state.newUser.lastname}
onChange={AgentsActions.updateLastname} />
</div>
<h4>Address</h4>
<button type='submit' className='btn btn-primary'>Submit</button>
</form>
<button onClick={this.onClick} className='btn btn-primary'>Submit</button>
</div>
</div>
</div>
)
}
}
export default Agents;
I do not know if I am missing something in my code or there is an error but i am kind of desperate. Also I am basing my code on another file that i have which is working just fine...
Thank you in advance for your time

Resources