Idiomatic way of making a thunk action available to components? - react-redux

I have written the following thunk action creator which is used to make a request to an api.
If my understanding is correct, the thunk action creator will be handled by middleware and have access to the store dispatch method.
What is the idiomatic way of making this thunk action creator available to a react component?
The best method I can think of is just importing the thunk action creator directly.
export function fetchMovie(title) {
return (dispatch) => {
dispatch(requestMovie(title));
const url = `http://www.omdbapi.com/?t=${title}&y=&plot=short&r=json`
return axios.get(url)
.then(response => {
dispatch(receiveMovie(title, response.data))
})
.catch(err => dispatch(requestMovieErr(title, err)))
}
}

Yes your assumption is correct. The most common way would be to import individual action functions into your components as needed, such as:
import React, { PropTypes, Component } from 'react';
import { connect } from 'react-redux';
// step 1: import action 'fetchMovie'
import { fetchMovie } from './actions/whateverYourFileIsCalled';
class SomeComponent extends Component {
render() {
return (
<div>
{/*
step 3: use the 'fetchMovie' action
that is now part of the component's props
wherever we'd like
*/}
<button onClick={this.props.fetchMovie.bind(null, 'Jurassic Park')}>
Click here for dinosaurs
</button>
</div>
);
}
}
SomeComponent.propTypes = {
fetchMovie: PropTypes.func.isRequired
};
export default connect(
{},
{
// step 2:
// connect the 'fetchMovie' action to this component's props using redux helper
fetchMovie
}
)(SomeComponent);

Related

How to clean from the values?

I'm using a laravel-vue-boilerplate .In the package there is User CRUD. I made a same thing,copy/paste,change couple of details to have Item CRUD.Working fine. The issue is after action (edit) I want to add a new Item,the form is filled already with the edited Item values. The form is in a modal which is a component.
Don't know which part of the code I paste here,Looking forward!
Modal :
addItem(): void {//this is the actions to call the modal
this.isModalAdd = true;
this.setModalVisible(true);
this.form=this.new_form;
}
edit(item:Item):void{
this.isModalAdd = false;
this.setModalVisible(true);
this.form = { ...item };
}
<ItemsModal v-bind:form='form' v-bind:is-add='isModalAdd' v-bind:is-visible='isModalVisible' ></ItemsModal>//added in the Items template
<script lang="ts">//Items Modal
import { Component, Emit, Prop, Vue } from 'vue-property-decorator';
import { Action, State, namespace } from 'vuex-class';
import checkPassword from '#/utils/checkPassword';
const iStore = namespace('items');
#Component
export default class ItemsModal extends Vue {
#Prop() form;
#Prop() isAdd;
#Prop() isVisible;
#iStore.Action addItem;
#iStore.Action editItem;
#iStore.Action setModalVisible;
#iStore.State isModalLoading;
handleOk() {
if (this.isAdd) {
this.addItem(this.form);
} else {
this.editItem(this.form);
}
}
handleClose() {
this.setModalVisible(false);
}
}
</script>
<template lang="pug">
b-modal(
hide-header-close=true,
:visible='isVisible',
:cancel-title='$t("buttons.cancel")',
:ok-disabled='isModalLoading',
:ok-title='isModalLoading ? $t("buttons.sending") : isAdd ? $t("buttons.add") : $t("buttons.update")',
:title='isAdd ? $t("users.add_user") : $t("users.edit_user")',
#hide='handleClose',
#ok.prevent='handleOk',
)
b-form
b-form-group(
:label='$t("strings.name")'
label-for='name',
)
b-form-input#name(
type='text',
v-model='form.name',
maxlength='191',
required,
)
</template>
Your code seems incomplete to me. As per my guess, after your form submit, you should empty your form data. Means, at the end of addItem(this.form), this.editItem(this.form), setModalVisible(false) these methods, You should empty your this.form data or nullified form's properties. Like,
this.form = {}
or
this.form.name = null
After completing action from your api, try to empty or null your Datas related to that form.
editItem (form) {
// work with your backend
this.form = {}
}

React - Redux Form - handleSubmit

Looking for some insight with the Redux Form handleSubmit function. I am following along to Set Griders course from Udemy. Everything is running smoothly until here. Other error/bugs I was able to research and solve them myself. I am really struggling to find a solution here.
singin.js
import React, { Component } from 'react'
import { reduxForm } from 'redux-form'
import { Checkbox, Form, FormField, Button, Grid, GridColumn, Input } from 'semantic-ui-react'
class SignIn extends Component {
onFormSubmitHandler({ email, password }) {
console.log('Values', email, password)
}
render() {
// this.props are brought to us by reduxForm. Allowing to grab the field inputs and submit action
const { handleSubmit, fields: { email, password } } = this.props
return (
<Grid centered columns={2}>
<GridColumn>
<Form onSubmit={handleSubmit(this.onFormSubmitHandler.bind(this))}>
<FormField>
<label>Email:</label>
<Input placeholder='Email' {...email} />
</FormField>
<FormField>
<label>Password:</label>
<Input placeholder='Password' {...password} />
</FormField>
<FormField>
<Checkbox label='I agree to the Terms and Conditions' />
</FormField>
<Button action='submit'>Sign In</Button>
</Form>
</GridColumn>
</Grid>
)
}
}
export default reduxForm({
form: 'signin',
fields: ['email', 'password']
})(SignIn)
rootReducer
import { combineReducers } from 'redux'
import SetsReducer from './SetsReducer'
import { reducer as form } from 'redux-form'
const rootReducer = combineReducers({
sets: SetsReducer,
form
})
export default rootReducer
The console.log inside onFormSubmitHandler logs: 'Values undefined undefined'
Edit: Project on GitHub
I solved the issue. The fix involved importing 'Field' from redux-form and replacing the input element with elements. Passing {Input} as the component to render

redux-form not populating FieldArray with InitialValues object

I'm working with react-redux-form 7.03 and attempting to populate fields from a database. In the interests of simplifying to determine where my issue is, I've created a static array of 1 object to try to get information to display.
I'm fairly new to React and Redux, and I've been fighting this for three days now. I've been using the examples from https://codesandbox.io/s/qDjw3zGG and the redux-form examples with no luck.
Basic issue is that in the props, I'm seeing initialValue:xmlConfigDataSet with the correct information in the object (array with 1 object correctly filled in). However that is not being passed to the fields property to RenderRows. Debugging fields shows a length of 0 and it just passes over the map() and never renders.
import React, {Component} from 'react'
import {connect} from 'react-redux'
import { Field, FieldArray, reduxForm } from 'redux-form'
class XMLMatchUpForm extends Component {
constructor(props){
super(props);
this.state = {}
}
render(){
return (
<FieldArray name="xmlConfigDataSet" component={RenderRows} />
)
};
const RenderRows = ({fields, meta}) => (
<div>
{fields.map((field, index) => {
const value = fields.get(index);
return <RenderStaticField name={field} value = {value} />
})}
</div>
)
const RenderStaticField = (props) => {
<Field name={`${props.name}.MW_MD_VALUE`} component="input" />
}
XMLMatchUpForm = reduxForm({
form: "XMLMatchUpForm"
}) (XMLMatchUpForm);
function mapStateToProps(state) {
return {
initialValues: {
xmlConfigDataSet:[{"MW_XS_ID":1314,"MW_MD_ID":null,"MW_XDM_VALUE":"Silver Spring","MW_XS_TAG_NAME":"receivercity"}]
}
};
}
export default connect(mapStateToProps, {fetchXMLConfig}) (XMLMatchUpForm);
(ignore the {fetchXMLConfig} in the example, that is actually the action it is caling to get the xmlConfigDataSet, as I said above, I'm trying to simplify this to determine where the issue is.
Any ideas, or pointers to clear examples are welcome.

Error: cannot find name ionViewDidLeave

I'm not sure where I'm going wrong with ionViewDidLeave. I'm getting an error from the terminal that says "cannot find name ionViewDidLeave". Is there something I have to import to use it? I have already imported the navController.
Here is my
ts.file
import { Component } from '#angular/core';
import { NavController, ModalController } from 'ionic-angular';
import { EditPost } from '../edit-post/edit-post';
import { LoadingController } from 'ionic-angular';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class Home {
buttonColor: string = '#787083';
constructor (public navCtrl: NavController, public modalCtrl: ModalController, public loading: LoadingController) {
//OTHER FUNCTIONS
/*Navigate to edit page */
editPost(){
this.buttonColor = '#553481'; //change button background color on click
this.navCtrl.push(EditPost, {})
.catch(() => {
// Page requires authentication, re-direct to Login page
this.navCtrl.setRoot(Login, {routeToPage: 'EditPost'});
});
ionViewDidLeave(){
this.buttonColor = '#787083';
};
}// end of editPost()
}//close class
HTML
<ion-footer class="footer">
<ion-segment small class="footer">
<ion-segment-button id="post" value="post" (click)="postEvent()" [ngStyle]="{'background-color': buttonColor}" small> <span class="footer">NEW POST</span></ion-segment-button>
<ion-segment-button id="edit" value="edit" (click)="editPost()" [ngStyle]="{'background-color': buttonColor}" small> <span class="footer">Edit Post</span></ion-segment-button >
</ion-segment>
</ion-footer>
When you write inside an method
ionViewDidLeave()
you are calling a function from the current scope (editPost) function. The right way to call from the object would be:
this.ionViewDidLeave()
but I guess it's not right to call it (ionViewDidLeave is part of Ionic's page lifecycle), and I guess too that what you want to do is define this method and you have a type in your code. The right code should be:
export class Home {
buttonColor: string = '#787083';
constructor (public navCtrl: NavController, public modalCtrl: ModalController, public loading: LoadingController) {
editPost(){
this.buttonColor = '#553481'; //change button background color on click
this.navCtrl.push(EditPost, {})
.catch(() => {
// Page requires authentication, re-direct to Login page
this.navCtrl.setRoot(Login, {routeToPage: 'EditPost'});
});
}// end of editPost()
ionViewDidLeave(){
this.buttonColor = '#787083';
};
}//close class

Redux-Form: Not able to change value of input elements

I've been trying to implement a form in MapsAddrForm.jsx using Redux-Form and I can't seem to change the value of my input element. When the page loads, the input element does not respond to keyboard input, and when the form field submits, it returns an empty object to the parent component DistrictFinder. Beyond these two files, I've also added form:formReducer as an argument to combineReducers much like the simple example in the Redux-Form tutorials. Is there any way to restore the ability for the DistrictFinder to receive data objects from the address form? For reference, I'm using React 15.1.0, React-redux 4.4.5, ES6, and Redux-Form 5.3.1, all compiled using Webpack.
MapsAddrForm.jsx
import React, {Component} from 'react';
import { connect } from 'react-redux';
import {reduxForm} from 'redux-form';
class MapsAddrForm extends Component {
constructor(props) {
super(props);
}
render() {
const {fields: {address,address2}, handleSubmit} = this.props;
return (
<form onSubmit={handleSubmit}>
<div>
<input type="text" placeholder="Your address" {...address}/>
</div>
<button type="submit">Submit</button>
</form>
);
}
}
export default reduxForm({
form: 'addressForm',
fields: ['address']
})(MapsAddrForm);
DistrictFinder.jsx
import React, { Component, PropTypes } from 'react'
import MapsAddrForm from './MapsAddrForm.jsx'
import { connect } from 'react-redux'
import { changeAddress } from '../actions/index.jsx'
class DistrictFinder extends Component {
constructor(props) {
super(props);
this.handleAddrSubmit = this.handleAddrSubmit.bind(this);
}
handleAddrSubmit(data) {
console.log("Address received: " + JSON.stringify(data));
}
render() {
const {address, district} = this.props
return (
<div class="GMaps">
<h1>Find your district!</h1>
<MapsAddrForm onSubmit={this.handleAddrSubmit} />
<p>My district number is: {district}</p>
</div>
);
}
}
DistrictFinder.propTypes = {
district: PropTypes.string.isRequired,
dispatch: PropTypes.func.isRequired
};
function mapStateToProps(state) {
const { district } = state.infoChange;
return {
district
};
};
export default connect(mapStateToProps)(DistrictFinder);
I ran into this as well on redux-form#6.2.0
After looking at the source for one of the examples, I noticed the call to combineReducers has an explicit key "form" to the object argument. When I added this explicit key, the fields became editable.
// Correct
const reducer = combineReducers({
form: reduxFormReducer // mounted under "form"
})
If you have an existing app, like I do, you likely have this style es6 syntax from the various redux starters.
// Incorrect: results in the witnessed bad behavior
const reducer = combineReducers({
reduxFormReducer
})
See the combineReducers call at https://github.com/erikras/redux-form/blob/master/examples/simple/src/index.js#L10
It'd be interesting to see if this could be a constant that could be passed in and leveraged by the lib. "form" feels like a easily corruptible "namespace".

Resources