In a React-Redux app, I'm trying to pass the form variable from a component to the action creator without using Redux-Form, Formic or any other extension.
myForm.js
import { connect } from "react-redux";
import { fetchData } from "../actions/myActions";
class myForm extends Component {
constructor(props) {
super(props);
this.state = {
from: "",
to: ""
};
this.onFormSubmit = this.onFormSubmit.bind(this);
}
onFormSubmit(event) {
event.preventDefault();
const from = event.target.elements.from.value;
const to = event.target.elements.to.value;
this.setState({
from: from,
to: to
});
this.props.fetchData(
this.state.from,
this.state.to,
);
}
render() {
return (
<div>
<form onSubmit={this.onFormSubmit}>
<div>
<input
type="text"
name="from"
/>
</div>
<div>
<input
type="text"
name="to"
/>
</div>
</form>
</div>
export default connect(
null,
{ fetchData }
)(myForm);
I'm passing the action creator fetchData to myForm component and on form submit invoke the onFormSubmit function, which passes the form variables to fetchData like this:
this.props.fetchData(
this.state.from,
this.state.to,
);
Then inside myActions.js I try to access those form variables and start an API request.
myActions.js
import { FETCH_DATA } from "./types";
import axios from "axios";
const APP_KEY = <my api key>;
export const fetchData = (from,to) => async dispatch => {
const response = await axios.get`${URL}/${from}/to/${to}?app_key=${APP_KEY}`;
dispatch({
type: FETCH_DATA,
payload: response.data.journeys
});
};
Is what I'm trying the right approach?
Unfortunately it seems that variables from and to don't get passed to the action creator inside myAction.js.
setState is an async operation, hence this.props.fetchData is called, before the state is even set. You need to use the callback in the second argument of setStatein myForm.js which is excuted after the state has been updated.
this.setState({
from: from,
to: to
}, () => {
this.props.fetchData(this.state.from,this.state.to)
});
Hope this helps. Happy coding !
Related
this is my index code and I want to transfer / send this data to another page
I try this code but it's not working I don't why
import { useRouter } from "next/router";
function index() {
const router = useRouter();
const [inputValue, setInputValue] = useState("");
const handleImgInput = (e) => {
e.preventDefault();
router.push({
pathname: "/createproject/uploadnfts/nftdetails",
query: inputValue,
});
};
return (
<form action="" onSubmit={handleImgInput}>
<input
type="email"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
</form>
);
}
this is the data page code. when this page open, I didn't receive the input value from my index page and also what i input in the index file, it appears to my localhost link like this:
http://localhost:3001/data?demo#gmail.com
import { useRouter } from "next/router";
import React from "react";
function Data() {
const router = useRouter();
const {
query: { inputValue },
} = router;
const props = {
inputValue,
};
console.log(props);
return (
<div>
<p>{inputValue}</p>
</div>
);
}
export default Data;
The query property in the router options requires passing an object to it. The query params are formed from the key-value pairs in the object.
See below example from the Next.js docs:
import { useRouter } from 'next/router'
export default function ReadMore({ post }) {
const router = useRouter()
return (
<button
type="button"
onClick={() => {
router.push({
pathname: '/post/[pid]',
query: { pid: post.id },
})
}}
>
Click here to read more
</button>
)
}
Taken from: https://nextjs.org/docs/api-reference/next/router#with-url-object
In your case, this would be something like:
const handleImgInput = (e) => {
e.preventDefault();
router.push({
pathname: "/createproject/uploadnfts/nftdetails",
query: { value: inputValue },
});
};
When you want to access the query params, make sure to reference them like an object's properties. For the example above, this would be:
const { value } = router.query;
I'm doing a Redux tutorial, and don't understand something in it. I have the following container:
import React, { Component } from 'react';
import CommentsList from "./comments_list";
import { connect } from 'react-redux';
import * as actions from '../actions';
class CommentBox extends Component {
constructor(props) {
super(props);
this.state = { comment: '' };
}
handleChange(event) {
this.setState({ comment: event.target.value })
}
submitButton(e) {
e.preventDefault();
this.props.saveComment(this.state.comment);
this.setState({ comment: '' });
}
render () {
return(
<div>
<form onSubmit={(e) => this.submitButton(e)} className="comment-box">
<textarea
value={this.state.comment}
onChange={(e) => this.handleChange(e)} />
<button action="submit">Submit</button>
</form>
<CommentsList comment={this.state.comment}/>
</div>
);
}
}
export default connect(null, actions)(CommentBox);
This container uses:
import * as actions from '../actions';
and on the bottom of the file:
export default connect(null, actions)(CommentBox);
I'm used to using mapStateToProps and mapDispatchToProps, but here only the actions are imported, and then used in the submitButton(e) method:
this.props.saveComment(this.state.comment);
The saveComment comes from the actions/index.js file:
import { SAVE_COMMENT } from './types';
export function saveComment(comment) {
return {
type: SAVE_COMMENT,
payload: comment
}
}
Can I always use this.props to call a function from the actions/index.js file? Why don't I need to use the mapStateToProps first?
Can I always use this.props to call a function from the actions/index.js file?
Yes. From the react-redux docs:
[mapDispatchToProps(dispatch, [ownProps]): dispatchProps] (Object or Function): If an object is passed, each function inside it is assumed to be a Redux action creator. An object with the same function names, but with every action creator wrapped into a dispatch call so they may be invoked directly, will be merged into the component’s props.
connect is wrapping the exports from actions/index.js with dispatch calls for you.
Why don't I need to use the mapStateToProps first?
Because mapStateToProps and mapDispatchToProps are used for different purposes and mapped separately before being merged together and injected into your component.
If either return undefined or null, they are ignored. In the case of mapStateToProps, it also means the component won't subscribe to updates from the store. Again, from the react-redux docs:
If you don't want to subscribe to store updates, pass null or undefined in place of mapStateToProps.
currently I'm trying to use redux-form/immutable in ma brand new project and I'm facing so problems which I can't resolve (aspecially, with this cool reducer.plugin), probably couse I'm kinda new to immutable and stuff like that.
Appreciating and advice or help on topic.
so, first of all:
import { combineReducers } from 'redux-immutable';
import routing from './routeReducer';
import { reducer as formReducer } from 'redux-form/immutable';
const rootReducer = combineReducers({
routing,
form: formReducer.plugin({
signupForm: (state, action) => {
switch(action.type) {
case 'SIGNUP_FACEBOOK':
return {
console.log(state);
}
default:
return state
}
}
})
});
while non of the forms field and touched this console.log(state) returns undefined, so here is the question: how may I initialize the form with some kind of state and actually update in on some sort of action ('SIGNUP_FACEBOOK' for this example)?
here is the form code sample:
import React, { PropTypes } from 'react';
import { Field, reduxForm } from 'redux-form/immutable';
import renderInputControl from '../../../common/InputControl';
import validate from './validate';
const SignUpForm = (props) => {
const { handleSubmit, submitting, invalid } = props;
return (
<form onSubmit={handleSubmit} className='signup__form'>
<Field name='name' type='text' component={renderInputControl} label='Full Name'/>
<Field name='email' type='email' component={renderInputControl} label='Email'/>
<Field name='password' type='password' component={renderInputControl} label='Password'/>
<Field name='tel' type='tel' component={renderInputControl} label='Phone Number'/>
<button type='submit' disabled={submitting || invalid}>Sign Up</button>
</form>
);
};
SignUpForm.propTypes = {
handleSubmit: PropTypes.func.isRequired,
onSubmit: PropTypes.func,
submitting: PropTypes.bool,
invalid: PropTypes.bool
};
export default reduxForm({
form: 'signupForm',
validate
})(SignUpForm);
and the second question is: even after I touch any of the fields (so it actually get some state, e.g. console.log(state) returns Map...), how should I update it, couse code:
state.getIn(['fields', 'name']).set('visited', false).set('touched', false)
which should set 'touched' and 'visited' to false doesn't do anything?
Best of luck!
Newbie here trying to learn some Redux.
GOAL: to get a button to click and login/logout, updating the store as true/false status whichever way.
const store = createStore(myReducer)
Created my store, passing in my reducer.
This has a default state of logged out. And returns the opposite, whenever the button is clicked.
I know this action works through debugging.
function myReducer(state = { isLoggedIn: false }, action) {
switch (action.type) {
case 'TOGGLE':
return {
isLoggedIn: !state.isLoggedIn
}
default:
return state
}
}
The problem starts here - when i try to access the store.getState() data.
class Main extends React.Component {
render() {
return (
<div>
<h1>Login Status: { state.isLoggedIn }</h1>
<button onClick={this.props.login}>Login</button>
</div>
)
}
}
const render = () => {
ReactDOM.render(<Main status={store.getState().isLoggedIn} login={() => store.dispatch({ type: 'TOGGLE' })}/>, document.getElementById('root'));
}
store.subscribe(render);
render();
I've tried store.getState().isLoggedIn & store.getState() & this.props.status and then assigning the store.getState().isLoggedIn in the Main component - but nothing works.
Can anyone tell me where i'm going wrong?
You don't directly access the store using getState to find data. The Redux docs explain the process in-depth, but basically you'll connect each component to the Redux store using connect method of the react-redux package.
Here's an example of how this could work for your above component:
import React, { Component } from 'react'
import { connect } from 'react-redux'
import Main from '../components/Main'
class MainContainer extends Component {
render() {
return <Main {...this.props} />
}
}
const mapStateToProps = state => ({
isLoggedIn: state.isLoggedIn,
})
const mapDispatchToProps = dispatch => ({
login() {
dispatch({type: 'TOGGLE'})
},
})
MainContainer = connect(
mapStateToProps,
mapDispatchToProps,
)(MainContainer)
export default MainContainer
You would then want to render the MainContainer in place of the Main component. The container will pass down isLoggedIn and login as props to Main when it renders it.
Redux action changePictogramsKeyword is not being fired.
This is the file where I define my action and reducer (redux/module/keyword.js):
export const CHANGE_PICTOGRAMS_KEYWORD = 'CHANGE_PICTOGRAMS_KEYWORD'
export function changePictogramsKeyword (keyword) {
return {
type: CHANGE_PICTOGRAMS_KEYWORD,
keyword
}
}
// Updates error message to notify about the failed fetches.
export default function pictogramsKeyword (state = '', action) {
switch (action.type) {
case CHANGE_PICTOGRAMS_KEYWORD:
return action.keyword
default:
return state
}
}
My root reducer:
import { combineReducers } from 'redux'
import { routerReducer as router } from 'react-router-redux'
import locale from './modules/locale'
import errorMessage from './modules/error'
import pictogramsKeyword from './modules/keyword'
export default combineReducers({
locale,
router,
pictogramsKeyword,
errorMessage
})
So with the devTools I can check that my initialState is as I expected from the rootReducer:
locale:"en"
router:{} 1 key
pictogramsKeyword:""
errorMessage:null
This is the code of the view where I connect to Redux Store. Component SearchBox is in charge of firing the action changePictogramsKeyword:
import React, {Component, PropTypes} from 'react'
import SearchBox from 'components/SearchBox.js'
import { connect } from 'react-redux'
import { changePictogramsKeyword } from 'redux/modules/keyword'
class SearchPictogramsView extends Component {
handleDismissClick (e) {
this.props.resetErrorMessage()
e.preventDefault()
}
render () {
const { children, inputValue } = this.props
return (
<div>
<SearchBox value={inputValue} onChange={changePictogramsKeyword} />
{children}
</div>
)
}
}
SearchPictogramsView.propTypes = {
inputValue: PropTypes.string.isRequired,
children: PropTypes.node
}
function mapStateToProps (state, ownProps) {
return {
errorMessage: state.errorMessage,
inputValue: state.pictogramsKeyword
}
}
export default connect(mapStateToProps, {
resetErrorMessage, changePictogramsKeyword
})(SearchPictogramsView)
This is the code of the SearchBox component. AutoComplete is a material-ui component. onUpdateInput method gets fired everytime I press a key, however changePictogramsKeyword is not being fired (i see nothing through the dev tools)
import React, {Component, PropTypes} from 'react'
import AutoComplete from 'material-ui/lib/auto-complete'
import RaisedButton from 'material-ui/lib/raised-button'
class SearchBox extends Component {
constructor (props) {
super(props)
this.handleUpdateInput = this.handleUpdateInput.bind(this)
}
handleUpdateInput = (t) => {
console.log(t)
this.props.onChange(t)
}
render () {
return (
<div>
<AutoComplete onUpdateInput={this.handleUpdateInput} searchText={this.props.value} />
</div>
)
}
}
SearchBox.propTypes = {
value: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired
}
export default SearchBox
Right now, your action only gets called, but not dispatched because you're not mapping the actions correctly in the connect() call. (see the official documentation for more information)
In your SearchPictogramsView, change the mapDispatchToProps function of the connect() call to return an object with the wrapped functions:
export default connect(mapStateToProps, (dispatch) => {
return {
resetErrorMessage: () => dispatch(resetErrorMessage()),
changePictogramsKeyword: () => dispatch(changePictogramsKeyword())
};
})(SearchPictogramsView)
You can clean it up by making mapDispatchToProps its own function too:
function mapDispatchToProps(dispatch) {
return {
resetErrorMessage: () => dispatch(resetErrorMessage()),
changePictogramsKeyword: () => dispatch(changePictogramsKeyword())
};
}
export default connect(mapStateToProps, mapDispatchToProps)(SearchPictogramsView)
Let me know if that works!
It was really in the docs:
If an object is passed, each function inside it will be assumed to be
a Redux action creator. An object with the same function names, but
with every action creator wrapped into a dispatch call so they may be
invoked directly, will be merged into the component’s props
When I wrote:
<SearchBox value={inputValue} onChange={changePictogramsKeyword} />
Now is:
<SearchBox value={inputValue} onChange={this.props.changePictogramsKeyword} />
So I really call the dispatch of the action and not just the action!