What is making our Action Creator available as props in React? - reactjs

I have this code:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { fetchWeather } from '../actions/index';
class SearchBar extends Component {
constructor(props) {
super(props);
this.state = { term: '' };
this.onInputChange = this.onInputChange.bind(this);
this.onFormSubmit = this.onFormSubmit.bind(this);
}
onInputChange(event) {
this.setState({ term: event.target.value });
}
onFormSubmit(event) {
event.preventDefault();
this.props.fetchWeather(this.state.term);
this.setState({ term: '' });
}
render() {
return (
<form onSubmit={this.onFormSubmit} className="input-group">
<input
placeholder="Get a five-day forecast in your favorite cities"
className="form-control"
value={this.state.term}
onChange={this.onInputChange}
/>
<span className='input-group-btn'>
<button type="submit" className="btn btn-secondary">
Submit
</button>
</span>
</form>
);
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ fetchWeather }, dispatch)
}
export default connect(null, mapDispatchToProps)(SearchBar);
At one point in this tutorial, the author says that mapDispatchToProps hooks up our Action Creator fetchWeather to our component. But does it? Doesn’t it dispatch the action from our Action Creator to our reducers?
What is actually making this available in our component as props?

You can have
const mapDispatchToProps = { fetchWeather };
instead of
function mapDispatchToProps(dispatch) {
return bindActionCreators({ fetchWeather }, dispatch)
}
Anyway this is the block that makes your action creator available via this.props.

Related

dispatch is not defined no-undef

i am new to redux and i'm trying to dispatch an action from inside a function but it says "dispatch is not defined no-undef". Can anyone tell me what i'm doing wrong
import React, { Component } from 'react';
import {connect} from 'react-redux';
import {getYear} from '../actions/getYearAction';
import {requestYear} from '../actions';
class SigninForm extends Component {
constructor(props){
super(props);
this.state = {
car :{
year: '',
brand:[],
}
}
}
getYear(){
return this.props.year.map( (year, id) => {
return(
<option value={year} key={id}>{year}</option>
)
})
}
handleSelection(e){
dispatch(requestYear( e.target.value ));
}
render() {
return (
<div>
<form>
<select onChange = {this.handleSelection.bind(this)}>
<option value="" selected disabled hidden>--YEAR--</option>
{this.getYear()}
</select><br/>
<select>
<option value='' selected disabled>-Brand-</option>
</select>
<button type='submit'>Add</button>
{this.props.value}
</form>
</div>
);
}
}
function mapStateToProps(state){
return{
year: state.year
}
}
export default connect( mapStateToProps)(SigninForm);
i have to make an api call with the year selected from saga but i don't know how to do it.
here's my action
import { createAction } from 'redux-actions';
export const REQUEST_YEAR = 'REQUEST_YEAR';
export const requestYear = createAction(REQUEST_YEAR);
Please help me
Thank you
dispatch is a prop just like your other props, you need to call it with this.props.dispatch.
Also it's advisable to only pass in the actions that you want to call as props:
handleSelection(e) {
this.props.requestYear( e.target.value );
}
...
export default connect( mapStateToProps, { requestYear })(SigninForm);

React redux - update object state with new value

I use Redux for the first time, and I don't success to update a state. Here is the workflow : User logged in the app (/login), a token is stored in sessionStorage, and I dispatch user info to access userData in the /dashboard/:id page.
Right now when I console.log props on the Dashboard components, here is the result :
userData : {}
So the object userData is still empty.
Here is the code :
LoginForm :
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as authActions from '../../actions/authActions';
class LoginForm extends Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: '',
errors: {},
isLoading: false,
};
}
onChange(e) {
this.setState({
[e.target.name]: e.target.value
})
}
onSubmit(e) {
e.preventDefault();
this.setState({ errors: {}, isLoading: true });
this.props.actions.logInUser( { data: { user: { email: this.state.email, password: this.state.password }}})
}
render() {
return(
<div>
<form onSubmit={this.onSubmit.bind(this)}>
<div className="field">
<label className="label"> Email </label>
<div className="control">
<input type="email"
name="email"
value={this.state.email}
onChange={this.onChange.bind(this)}
className="input" />
</div>
</div>
<div className="field">
<label className="label"> Mot de passe </label>
<div className="control">
<input type="password"
ref="password"
name="password"
value={this.state.password}
onChange={this.onChange.bind(this)}
className="input" />
</div>
</div>
<div className="form-group">
<input type="submit" value="Signup" className="button is-primary" />
</div>
<Link to={{ pathname: '/register' }}>Inscription</Link>
</form>
</div>
);
}
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(authActions, dispatch)
};
}
export default connect(null, mapDispatchToProps)(LoginForm);
The authAction :
import { browserHistory } from 'react-router';
import * as types from './types';
import sessionApi from '../api/SessionApi';
export function loginSuccess(userData) {
return {
type: types.LOG_IN_SUCCESS,
payload: userData
}
}
export function loginFailed() {
return {
type: types.LOG_IN_FAILED
}
}
export function logInUser(credentials) {
return function(dispatch) {
return sessionApi.login(credentials)
.then(response => {
console.log(response);
if(response.data) {
sessionStorage.setItem('jwt', response.data.authentication_token);
dispatch(loginSuccess(response.data));
browserHistory.push('/dashboard/' + response.data.id);
} else {
dispatch(loginFailed());
}
})
.catch(error => {
throw(error);
})
}
}
The API :
import axios from 'axios';
class SessionApi {
static login(credentials) {
return axios.post('<link_hidden>', credentials)
.then(response => {
console.log(response);
return response;
})
.catch(error => {
return error;
});
}
}
export default SessionApi;
The session reducer (I have a rootReducer for combineReducer) :
import * as types from '../actions/types';
import initialState from './initialState';
export default function sessionReducer(state = initialState, action) {
switch(action.type) {
case types.LOG_IN_SUCCESS:
console.log(action);
return {
...state,
userData: action.payload
}
case types.LOG_IN_FAILED:
console.log('login failed');
break;
default:
return state;
}
}
and the initialState file :
export default {
session: !!sessionStorage.jwt,
userData: {}
}
Does someone know why the userData object is still empty ? In the session reducer I pass the userData so I don't understand ..
EDIT
dashboard.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
class Dashboard extends Component {
constructor(props) {
super(props);
console.log(props);
}
render() {
return(
<div>
<h1> Hello user </h1>
</div>
);
}
}
function mapStateToProps(state) {
const userData = state.sessionReducer.userData;
return { userData };
}
export default connect(mapStateToProps)(Dashboard);
Your code seems fine to me. Could you check your router if it's working correctly?
Try and change your userData to array, I think your payload may be an array of objects. That is:
export default {
session: !!sessionStorage.jwt,
userData: [ ]
}

Function is not a function inside React-Router

i create simple signin form for my website.
But i have a little problem.
When i type login and password and try send it to server i got error
Uncaught TypeError: this.props.signinUser is not a function
import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form';
import * as actions from '../../actions';
class Signin extends Component{
constructor(props){
super(props);
this.state= {};
this.onSubmit = this.onSubmit.bind(this);
}
renderField(field){
return(
<div className="form-group">
<label>{field.label}</label>
<input
className="form-control"
type="text"
{...field.input}
/>
</div>
);
}
onSubmit(e){
e.preventDefault();
let {login,password} = this.state;
//this.props.login(login,password);
console.log({login,password});
this.props.signinUser({login,password});
this.setState({
login: '',
password: ''
})
}
render(){
let {login,password} = this.state;
return(
<form onSubmit={this.onSubmit}>
<Field
label="Login:"
name="login"
component={this.renderField}
onChange={e => this.setState({login: e.target.value})}
/>
<Field
label="Password:"
name="password"
component={this.renderField}
onChange={e => this.setState({password: e.target.value})}
/>
<button action="submit" className="btn btn-primary"> Sign In </button>
</form>
);
}
}
function mapStateToProps(state) {
return { form: state.form };
}
export default reduxForm({
form: 'signin'
}, mapStateToProps, actions)(Signin);
And Actions.
import axios from 'axios';
export function signinUser({login,password}){
return function(dispatch){
axios.post('http://localhost:8080/auth', { login, password});
};
}
and finally reducer.
import { combineReducers } from 'redux';
import { reducer as fromReducer } from 'redux-form';
const rootReducer = combineReducers({
form: fromReducer
});
export default rootReducer;
I use react 16, react redux v5 and react-router v3, react form v7!
I think its problem with connect function from ReactForm!
The problem lies here
export default reduxForm({
form: 'signin'
}, mapStateToProps, actions)(Signin);
This should be
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
....
//remainder of the code
....
mapDispatchToProps =(dispatch)=>{
return {
actions : bindActionCreators(actions , dispatch)
};
}
Signin= connect(
mapStateToProps,
mapDispatchToProps
)(Signin);
export default reduxForm({
form: 'signin'
})(Signin);
You would need to change the call this.props.signinUser to this.props.actions.signinUser
More info at : https://redux-form.com/7.0.4/docs/faq/howtoconnect.md/

React + Redux Update issue

I have this class:
import React from 'react';
import {Link} from 'react-router';
import '../../styles/about-page.css';
import Item from './Item';
// Redux
import { connect } from 'react-redux';
import actions from '../../redux/actions';
import { bindActionCreators } from 'redux';
// Auth
import Auth from '../../modules/Auth';
import User from '../../constants';
class CommentForm extends React.Component {
constructor(props) {
super(props);
this.state = {
value: ''
};
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit() {
// alert('A name was submitted: ' + this.state.value);
this.props.onFinish({
name: this.props.user.name,
userId: this.props.user.id,
comment: this.state.value
});
}
render() {
if (!this.props.user || !this.props.user.name) return <div/>;
return (<div className="item">
{this.props.user.name}:
<label style={{width: '60%', margin: '10px'}}>
<input style={{width: '100%'}} type="text" value={this.state.value} onChange={this.handleChange.bind(this)} />
</label>
<input style={{width: '16%', display: 'inline-block', margin: '10px'}} type="submit" value="Enviar" onClick={this.handleSubmit.bind(this)}/>
</div>
);
}
}
#connect((state) => state)
class ItemPage extends React.Component {
constructor(props) {
super(props);
this.state = {
user: null,
pret: null
};
}
componentWillMount() {
let self = this;
this.props.actions.getPret(this.props.routeParams.id).then(function(a) {
self.setState({
pret: self.props.pret
})
});
if (Auth.isUserAuthenticated()) {
User.getBearer(Auth, function(info) {
self.setState({user: info});
});
}
}
onFinish(comment) {
//changing the state in react
//need to add '6' in the array
//create a copy of this.state.a
//you can use ES6's destructuring or loadash's _.clone()
const currentStatePretCopy = Object.assign({}, this.state.pret, { b: this.state.pret.comments.concat([comment])})
console.log(1, currentStatePretCopy);
currentStatePretCopy.comments.push(comment);
this.props.actions.updatePret(currentStatePretCopy);
}
render() {
let self = this;
if (!this.state || !this.state.pret) return <div/>;
return (<div>
<section>
<Item full={true} user={this.state.user} item={this.state.pret} actions={this.state.actions}/>
</section>
<div>
<CommentForm user={this.state.user} pret={this.state.pret} onFinish={this.onFinish.bind(this)}/>
{/* TODO: ad here */}
{this.state.pret.comments && this.state.pret.comments.length ? this.state.pret.comments.map(function (comment, index) {
return (<div className="item" key={index}> by <Link to={'/user/' + comment.userId}> #{comment.name} </Link> : {comment.comment} </div>);
}) : null}
</div>
</div>
);
}
}
function mapStateToProps (state) {
return {
pret: state.prets[0]
};
}
function mapDispatchToProps (dispatch) {
return {
actions: bindActionCreators(actions, dispatch)
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(ItemPage);
When i want to update the object, since props should be immutable, the documentation suggest cloning the object and put it in the state.
I am modifying the state with the new value and sending it to Redux actions but the system complains:
Uncaught Error: A state mutation was detected between dispatches, in the path 'prets.0.comments.1'. This may cause incorrect behavior.
Since i copy the object I do not know how should I update the store via React+Redux
The comments array is still the original one. So you are mutating the original object with push.
Replace
currentStatePretCopy.comments.push(comment);
With
currentStatePretCopy.comments = currentStatePretCopy.comments.concat([comment])
And everything should work fine

React redux do not propagate state

i don't know what im doing wrong.
I thought i got the point how to implement with redux.
My Problem is that the state of the Component is not propagating to the Component after the state changed. I know we have to create new state object and i'm sure im doing it right. But there is no changes.
And the other question is, why the state is in the object "resetLink" see image. Why is it not stored in the state Object?
Please help me to get it work.
Action for the redux:
export const sendResetLink = (email) => {
return {
type: RESET_LINK,
email
}
}
class App extends React.Component {
render() {
return <VisibleResetLink/>
}
}
This is where the magic with props and dispatch function happens:
import {connect} from 'react-redux';
import SendResetLink from '../components/SendResetLink.jsx';
import {sendResetLink} from '../actions/index'
import _ from 'lodash';
const mapDispatchToProps = (dispatch) => {
return {
onClick: (email) => {
dispatch(sendResetLink(email))
}
}
}
const mapStateToProps = (state) => {
console.log("actual state", state);
return _.assign({} , {state: state.resetLink});
}
const VisibleResetLink = connect(
mapStateToProps,
mapDispatchToProps
)(SendResetLink)
export default VisibleResetLink
This is the Component which load props but never rerender them when they change.
import React, { PropTypes } from 'react';
import ReactDOM from 'react-dom';
class SendResetLink extends React.Component {
constructor(props, email, result) {
super(props);
console.log('props',props);
this.onClick = props.onClick;
this.result = props.state.result;
this.input = props.state.email;
}
renderResult (result){
console.log("renderResult",result);
if(result)
return <div className="card-panel deep-orange lighten-4">Invalid username and password.</div>
}
render() {
return <div>
{this.renderResult(this.result)}
{this.result}
<form className="col s12" action="/login" method="post" onSubmit={e => {
e.preventDefault()
if (!this.input.value.trim()) {
return
}
this.onClick(this.input.value);
this.input.value = ''
} }>
<div className="row">
<div className="input-field col s12">
<i className="material-icons prefix">email</i>
<input id="email" type="email" name="email" className="validate" ref = {(node) => this.input = node } />
<label for="email">Email</label>
</div>
</div>
<div className="divider"></div>
<div className="row">
<div className="col m12">
<p className="right-align">
<button className="btn btn-large waves-effect waves-light" type="submit" name="action">Send reset key</button>
</p>
</div>
</div>
</form></div>
}
}
SendResetLink.propTypes = {
onClick: PropTypes.func.isRequired,
state: PropTypes.object.isRequired
}
export default SendResetLink
And this is the other relevant code snippet, where the reducer is implemented.
import _ from 'lodash';
const resetLink = (state = {email:"test#test.de", result:true}, action) => {
console.log('state', state)
switch (action.type) {
case 'RESET_LINK':
return _.assign({},state,{email: action.email, result: false});
default:
return state
}
}
export default resetLink;
import { combineReducers } from 'redux'
import resetLink from './ResetLinkReducer.jsx'
const resetApp = combineReducers({
resetLink
})
export default resetApp
import App from './components/app.jsx';
import {Provider} from 'react-redux';
import { createStore } from 'redux';
import { render } from 'react-dom';
import React from 'react';
import resetApp from './reducers/index.js';
let store = createStore(resetApp,{});
console.log(store)
render(<Provider store={store}>
<App />
</Provider>, document.getElementById('sendResetLinkComponent'));
console logs. After click the renderResult should change to false. But it stays on true
You set this.result in your SendResetLink component only once in the constructor, which is only executed when the component is instantiated. The constructor will not be executed every time the application's state changes:
this.result = props.state.result;
Instead of assigning this part of the state to an instance attribute of your react component, simply use the props attribute directly in your render() function:
{this.renderResult(this.props.state.result)}

Resources