I'm a newbie to the react-redux world, please correct me if I'm missing something:
Basically I'm trying to access the hostname via redux store and use it inside the form. As of now I'm just using the hostname reference window.location within the redux form but I would like to access the hostname via the redux store/ via any other better approach so I can keep the function pure? Thoughts?
Thanks
Edit:
import React from 'react'
import { Field, reduxForm } from 'redux-form'
const SimpleForm = (props) => {
const { handleSubmit, pristine, reset, submitting } = props
// goal is to prevent this here and do it via redux store or
// any other options
// so I can avoid external calls inside pure function
const hostName = window.location.hostname;
return (
<form onSubmit={handleSubmit}>
<div>
<label>First Name</label>
<div>
<Field name="firstName" component="input" type="text" placeholder="First Name"/>
</div>
</div>
</form>
)
}
export default reduxForm({
form: 'simple' // a unique identifier for this form
})(SimpleForm)
Question:
How can I store the window objects in the redux store and is it possible to access them in the redux forms via redux store or would react router help by any chance?
You should connect your form to the Redux state.
import { connect } from 'react-redux'
export default reduxForm({
form: 'simple' // a unique identifier for this form
})(connect(mapStateToProps)(SimpleForm)
and then:
const mapStateToProps = state => {
return {
hostName: state.reducerName.hostName
}
}
so you can access it through this.props.hostName, considering that hostName is set in the reducer previously.
If you are using React Router, you can access the history object through props, which also has the location property.
The previously mentioned answer did now work for me sadly,
Here is what worked for me, reference to react-redux doc
import { connect } from 'react-redux';
const mapStateToProps = state => {
return {
form: state.form
}
}
ClassName= connect(
mapStateToProps,
mapDispatchToProps
)(ClassName);
export default reduxForm({
form: 'ObjectRepo',
validate: validateObjects
})(ClassName);
The 'validate' that im passing is my validation.js, since i am using sync validations (if you have any, its completely optional)
'form' is the name of state i have used in my reducer. My reducer code,
const allReducers = combineReducers({
form: reduxFormReducer
});
Related
some questions about React.js and Redux:
Can functional components also take advantage of the store and the states saved therein? e.g maybe in combination with React hooks like useEffect()?
In general, I can combine multiple reducers to one rootReducer and createStore(rootReducer) with it, and then pass it to a Provider Component that wraps my Component with it, this way, the store should be globally available in my whole app, correct?
For every component that want to use the store / states, do I always have to import the 2 methods mapStateToProps() and mapDispatchToProps() from react-redux for every Component and then connect them? Or can I also do this on some top-level component and make the usage of redux available in all my components globally, like in question 2) with the store provider?
last question: Can I still use the this.state property in my Components or use them in parallel as an addition (e.g for this Component isolated states) and then get the props from this state as usual with this.state.someState or is this not possible anymore when I already use Redux? And in the same way, can I still use / pass props to my components and read them from my Components as well, or is everything managed by state now only? (Or has the passing of props to my children nothing to do with Redux)?
1) Yes functional components can take advantage of the store. Its arguably much cleaner to read since props can be destructured right away.
const MyComponent = ({ auth }) => {
const [display, setDisplay] = useState(false)
useEffect(() => {
if(auth.user){
setDisplay(true)
}
}, [auth.user])
return(
<div>
{ display ? "Content": "Please sign in" }
</div>
)
}
const mapStateToProps = (state) => {
return{
auth: state.auth
}
}
export default connect(mapStateToProps)(MyComponent)
2) That is correct. You can also use combineReducers() which in some ways is cleaner to read.
import { createStore, combineReducers } from "redux"
import authReducer from "./reducers/authReducer"
import postReducer from "./reducers/postReducer"
const store = createStore(combineReducers({
auth: authReducer,
post: postReducer
}))
export default store
Then import store, wrap your App.js in a Provider and give it a prop of that store.
3) Generally, if you want your component to have direct access to the store it is a recognized pattern to use connect() in each one. Whether you decide to use mapStateToProps() or mapDispatchToProps() is entirely dependent on what that component needs to do. It does not required that you use both, you can just define one or the other in the connect().
import React, { useState } from "react"
import { addPost } from "/actions/postActions"
import { connect } from "react-redux"
const Form = ({ addPost }) => {
const [text, setText] = useState("")
const handleSubmit = (e) => {
e.preventDefault()
addPost(text)
}
return(
<form onSubmit={handleSubmit}>
<input value={text} onChange={(e) => setText(e.target.value)}/>
</form>
)
}
const mapDispatchToProps = (dispatch) => {
return {
addPost: (text) => dispatch(addPost(text))
}
}
export default connect(null, mapDispatchToProps)(Form)
4) You might have noticed by now that in the context of components, redux-state is stored as props. They are entirely different and isolated streams of data. So state remains untouched and controlled by the component itself. You can still freely use methods like this.state.dog even when your component is connected to the store. This is the isolation between component-state and redux-state.
import React, { useState } from "react"
import { connect } from "react-redux"
class MyDogs extends React.Component{
state = {
dog: "Tucker"
}
render(){
return(
<div>
Component State Value: {this.state.dog} //Tucker
Redux State Value: {this.props.dog} //Buddy
</div>
)
}
const mapStateToProps = (state) => {
return {
dog: state.dog
}
}
export default connect(mapStateToProps)(MyDogs)
I have a redux-form that I'm connecting to the state as follows:
export default connect(mapStateToProps)(reduxForm({
form: 'MyForm',
validate // validation function given to redux-form
})(MyForm));
I now would like to get a slug value which is a react-router param (after navigating to the page like this:
browserHistory.push(`/mypage/${slug}`) ). However ownProps is empty:
const mapStateToProps = (state, ownProps) => {
console.dir(ownProps); // ISSUE: this is empty
const someField = selector(state, 'someField');
...
};
I tried different ways of connected to redux-form, but haven't been successful. Would really appreciate any hints on how to solve this.
I think I have understood your problem here. You need to connect the redux form in order to get values using selector. Please see example code below. Hope it will help you.
import React from 'react'
import { Field, reduxForm, formValueSelector } from 'redux-form'
import validate from './validate'
import example from './components/example'
import { connect } from 'react-redux'
function myForm ({
someProp,
exampleClick
}) {
function handleSubmit (e) {
}
return <form onSubmit={handleSubmit}>
<Field
name='name'
component={example}
submitBtnClicked={exampleClick} />
</form>
}
let thisForm = reduxForm({
form: 'myForm',
validate
})(myForm)
const selector = formValueSelector('myForm')
thisForm = connect(
state => {
const someField = selector(state, 'someField')
return ({someField})
}
)(thisForm)
export default thisForm
I solved it by adding react-router's withRouter around my connected component:
export default withRouter(connect(mapStateToProps)(reduxForm({
form: 'MyForm',
validate // validation function given to redux-form
})(MyForm)));
I am building a form using React and Redux Form. I use the formValueSelector to connect to several input fields. However, when I update these input fields in the UI, the form is not re-rendered. My code looks like this:
// HelpForm.jsx
import React from 'react';
import { connect } from 'react-redux';
import { reduxForm, formValueSelector } from 'redux-form';
import CategorySelectBlock from 'apps/help_form/components/CategorySelectBlock';
const selector = formValueSelector('help_form');
const mapStateToProps = state => ({
category: selector(state, 'category'),
subcategory: selector(state, 'subcategory')
});
class HelpForm extends React.Component {
render() {
const {
category,
subcategory
} = this.props;
console.log('Rendering HelpForm');
console.log('category:', category);
console.log('subcategory:', subcategory);
return (
<form id="helpform">
<CategorySelectBlock
category={category}
subcategory={subcategory}
/>
</form>
);
}
}
const ReduxHelpForm = reduxForm({
form: 'help_form'
})(HelpForm);
export default connect(
mapStateToProps
)(ReduxHelpForm);
I can see from my console logs that this HelpForm component is only rendering once, even after I select a new value for the category or subcategory fields (these are defined within the CategorySelectBlock component).
Am I doing something wrong, or misunderstanding how formValueSelector works? Any guidance would be much appreciated. Thank you!
I figured it out. I wasn't passing the onChange property through my custom input type.
So I'm trying to load a Redux Form pre populated with values from my store. However I'm unable to get anything back other than null. Been at this for several hours now and been reading over several SO examples trying different things and think I'm just at a wall on this.
Following this Redux Form Initializing from State example
Redux: 3.6.0
React-Redux: 5.0.3
Redux-Form: 6.6.3
React: 15.4.2
There is a parent component that is rendering a child component which is the form. For sake of brevity going to put in the bare minimum of code and make names as generic as possible. Everything loads fine but I think the issue relies in not properly connecting to my store. Or rather I should just be loading the data on a componentWillMount?
Parent Component:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchUser } from '../actions/usersActions';
import ChildForm from './ChildForm.jsx'
#connect((store) => {
return{
user: store.users.user
}
})
export default class Parent extends Component{
componentWillMount(){
this.props.dispatch(fetchUser(this.props.match.params.id))
}
submit = (values) => {//do some stuff}
render(){
return(
<ChildForm onSubmit={this.submit} />
);
}
}
ChildForm:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import { user } from './reducers/usersReducer.js';
class ChildForm extends Component{
render(){
console.log('FORM STATE >>>>>>>>>>', this.state); //Returns NULL
const { handleSubmit } = this.props;
return(
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="firstName">First Name</label>
<Field name="first_name" component="input" type="text"/>
</div>
<button type="submit">Submit</button>
</form>
);
}
}
ChildForm = reduxForm({
form: 'childForm',
enableReinitialize : true // I found this in another SO Example
})(ChildForm);
ChildForm = connect(
state => ({
user: state.user,
initialValues: state.user
}),
{ fetchUser }
)(ChildForm)
export default ChildForm;
enableReinitialize SO
usersReducer.js
export default function reducer(state={
user: {},
}, action){
switch (action.type){
case "FETCH_USER_FULFILLED":{
return{
...state,
user: action.payload
}
}
}
return state;
}
So this is where I'm at currently. Can get the page, form, and submit all work. However I can't seem to figure out how to get my Store values out and into the form fields. Any help would be greatly appreciated.
Looks like everything is wired up correctly but I wasn't pulling in the correct object in the store.
ChildForm = connect(
state => ({
initialValues: state.users.user
}),
{ fetchUser }
)(ChildForm)
...Always something little
I am able to render an input field using redux-form, but I can't seem to type any text inside the field (the component seems to re-render with each keystroke). Below is the code, I created a really simple one to try to pinpoint the problem:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { createStore, combineReducers } from 'redux';
import { Field, reduxForm, reducer as formReducer } from 'redux-form';
const reducers = {
// ... your other reducers here ...
form: formReducer // <---- Mounted at 'form'
};
const reducer = combineReducers(reducers);
const store = createStore(reducer);
class TestForm extends Component {
formSubmit(props) {
console.log('form submitted');
}
render() {
const { handleSubmit } = this.props;
return (
<div>
This is a test form.
<div>
<form onSubmit={handleSubmit(this.formSubmit.bind(this))}>
<Field name="firstField" component="input" type="text" />
<button type="submit">Submit</button>
</form>
</div>
</div>
);
}
}
TestForm = reduxForm({
form: 'testingForm'
})(TestForm);
export default TestForm;
I also literally copied and pasted the example from the official redux-form docs: http://redux-form.com/6.0.2/docs/MigrationGuide.md/ and I still encounter the same problem. Been trying to figure this out for some hours now. Could it be the version of redux or redux-form?
I'm using redux v3.5.2 and redux-form v6.2.1.
Would appreciate any help!
I had the same issue. The cause was that I failed to add the formReducer to the list of reducers I was using. To fix I added:
import { reducer as formReducer } from 'redux-form'
const RootReducer = combineReducers({
...
form: formReducer // <---- Mounted at 'form'
})
I think you need to look into the react-redux docs. This component needs to be placed inside a <Provider store={store}> component. Perhaps you are doing that outside this file and have just included the store initialization to be informative?