The state doesn't change in my react form. console.log(renderselect) doesn't have a value - it shows undefined.
I tried everything but there are no changes.
import React from "react";
import {Field, reduxForm} from "redux-form";
import {connect} from 'react-redux';
import {renderTextField, renderField, validate, warn, renderselect} from "../../../Components/Forms/renders"
class SyncValidationForm extends React.Component {
constructor(props) {
super(props);
this.state = {
errors: {},
opencheck: "hallo"
}
this.handleSelect = this.handleSelect.bind(this);
}
handleSelect = (value) => {
this.setState({"opencheck": value.target.value});
};
render() {
const {handleSubmit, pristine, reset, submitting, opencheck} = this.props;
console.log(opencheck);
return (<form onSubmit={handleSubmit}>
<div className="box-body">
<div className="row">
<div className="col-lg-3">
<Field className="form" name="favoriteColor" label="Maak eerste een keuze" component={renderselect} options={{
"Geen informatie" : 'Geen informatie',
'Altijd open' : 'Altijd open'
}}
onChange={this.handleSelect}/>
</div>
</div>
</div>
</form>);
}
};
SyncValidationForm = reduxForm({
form: 'insertstart', enableReinitialize: true,
warn
})(SyncValidationForm);
const mapStateToProps = state => {
state => ({
initialValues: { favoriteColor: 'Geen informatie' }
})
}
export default connect(mapStateToProps)(SyncValidationForm)
The selected box must change the state, but there are no changes. Also, in the first render, the opencheck in console log has no value.
You are conflating Redux state (which is mapped to your React component's props in mapStateToProps) with the built-in React component state which has nothing to do with Redux. Before continuing you may wish to read these links to fully understand the difference:
Redux: Organizing State
React: State and Lifecycle
It's often confusing and frustrating that both of these commonly-integrated libraries both call their data source "state", but it will be important for your to understand the difference and identify which one you are using.
If you want to use Redux to store the selected state, your handleSelect function needs to dispatch the new value to the Redux store, typically by using mapDispatchToProps to dispatch an action, not by calling this.setState
If you want to use React component state, then you need to refer to this.state.opencheck in your render() function, not this.props. opencheck.
Related
I am using react with redux and I have a todo-list.
I have a 'Todos' component which acts like a container and a 'Todoitem' component which holds every todo.
Everything works fine - reducers change the state and it is updating with new data, but the child component (aka 'Todoitem' component) won't re-render.
Todos.js:
import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import TodoItem from "./TodoItem";
class Todos extends Component {
render() {
return (
<div className="Todos">
<div className="todos_title"> {this.props.title} </div>
{this.props.todos.map(todo => {
console.log(todo); // this line prints updated data from state just fine!
return <TodoItem todo={todo} key={todo.id}></TodoItem>;
})}
</div>
);
}
}
// PropTypes
Todos.propTypes = {
todos: PropTypes.array.isRequired
};
const mapStateToProps = state => ({
todos: state.todosReducer.todos
});
export default connect(mapStateToProps)(Todos);
TodoItem.js:
import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { checkTodo } from "../actions/todosAction";
class TodoItem extends Component {
onChange = (e, id) => {
console.log(this.props.todo.completed.toString()); // this also prints fine the updated data
this.props.checkTodo(id); // dispatches an action to the reducer to toggle the todo.completed with the right todo.id
};
render() {
console.log('rendering'); // this is the problem - this line calls only in the first rendering, but not when state changes
let { id, title, completed } = this.props.todo;
return (
<div className={completed ? "TodoItemDone" : "TodoItem"}>
<p>
<input
className="todo_cb"
type="checkbox"
onChange={e => this.onChange(e, id)}
checked={completed ? "checked" : ""}
/>
{id}) {title}
</p>
</div>
);
}
}
// PropTypes
TodoItem.propTypes = {
todo: PropTypes.object.isRequired
};
const mapDispatchToProps = dispatch => ({
checkTodo: todo => dispatch(checkTodo(todo))
});
const mapStateToProps = state => ({});
export default connect(
null,
mapDispatchToProps
)(TodoItem);
I noticed that if I do pass a mapStateToProps in child comp it is re-rendering, like this:
const mapStateToProps = state => ({
some_prop: state
});
I understand the if I use mapStateToProps in the child it re-renders but I don't need anything directly from the state in child, the parent does this.
It makes some sense but my todos are stored in an Array in the state and I am mapping over them as you see in the parent component, so I can't map a specific todo from this array to the component props (how could I distinguish each element in the array to map to the prop?).
I am very confused.
I read that component re-renders when state or his props change. Inside the child component the props do change because the parent re-renders and it iterates the todos again and return the component with new props.
Maybe it's not the way to pass the todos to the components but I still don't understand how come the props changes and render() is not called.
Thank you very much!
Edit 1:
I connected the checkTodo action to the parent component and passed the function with props and it works just fine.
Still I don't understand why before the child component haven't re-rendered with the previous code...
Edit 2:
Actually I just lied, it does not work. I forgot to remove mapStateToProps which I said worked, so I am back to square one.
Edit 3:
Solved with by calling forceUpdate(). Still can't understand why it happened.
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
As a newbie in React and Redux, i'm trying to use react-dates in a component.
This is my code:
import * as React from 'react';
import { connect } from 'react-redux';
import { ApplicationState } from '../store';
import * as DateState from '../store/Date';
import * as SingleDatePicker from 'react-dates';
type DateProps = DateState.DateState & typeof DateState.actionCreators;
class DatePickerSingle extends React.Component<DateProps, any> {
public render() {
let { date } = this.props;
return (
<div>
<SingleDatePicker
id="date_input"
date={this.props.date}
focused={this.state.focused}
onDateChange={(date) => { this.props.user({ date }); }}
onFocusChange={({ focused }) => { this.setState({ focused }); }}
isOutsideRange={() => false}
displayFormat="dddd LL">
</SingleDatePicker>
</div>
);
}
}
export default connect(
(state: ApplicationState) => state.date,
DateState.actionCreators
)(DatePickerSingle);
This returns the following error:
Exception: Call to Node module failed with error: TypeError: Cannot read property 'focused' of null
focused an onFocusChange should receive the "datepicker state" as far as I understand.
Docs:
onFocusChange is the callback necessary to update the focus state in
the parent component. It expects a single argument of the form {
focused: PropTypes.bool }.
I think the problem is that I inject the DateState in the DatePickerSingle component, which doesn't know about focused state.
Is it possible to use my "own" state and the state from the DatePicker together? Or what is the best approach?
I'm trying for quite a while now, and I hope someone can help me with this.
UPDATE
The answer is quite simple: this.state is null because it has not been initialized. Just add
constructor() {
super();
this.state = {
focused: false
}
}
Anything coming from redux will be passed to your component as props, you can have component state in addition to that.
I have a decorated component with redux-form HOC and I want to access a Field value from the decorated component to enable/disable and hide/show other fields. what's the best approach to do that?
I tried to use Fields component to operate in the dependent fields but that hurts the performance of the decorated component as it provokes useless re-renders
It is also possible to connect the decorated component with redux and use formValueSelector that is provided by redux-form, but I wonder if there is a better approach to access a field(s) value.
Form Selectors and Field-dependent Values is described here. The solution is based on getFormValues:
import React from 'react';
import { connect } from 'react-redux';
import { reduxForm, getFormValues } from 'redux-form';
import FormComponent from './form.component';
export const FormContainer = props => {
const submitForm = (formValues) => {
console.log('submitting Form: ', formValues);
}
return (
<FormComponent
formValues={props.formValues}
change={props.change}
onSubmit={submitForm}
handleSubmit={props.handleSubmit}
/>
);
}
const mapStateToProps = state => ({
formValues: getFormValues('my-very-own-form')(state),
});
const formConfiguration = {
form: 'my-very-own-form',
}
export default connect(mapStateToProps)(
reduxForm(formConfiguration)(FormContainer)
);
and in your formComponent you can get the formValues from the props:
export const FormComponent = ({ handleSubmit, onSubmit, formValues }) => {
}
I am confused with props or state to use here. If I use state in #connect I get error and does not work. when I use props it does not work with onchange handler to set new props. Please help how should I make input working with state or props. I am retrieving initial data from api.
import React, {PropTypes} from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import { asyncConnect } from 'redux-async-connect';
import {load, isLoaded} from 'redux/modules/overview';
#asyncConnect([{
promise: ({ store: { dispatch, getState }, params: { id }, }) => {
const promises = [];
if (!isLoaded(getState())) {
promises.push(dispatch(load(id)));
}
return Promise.all(promises);
}
}])
#connect(
state => ({
overview: state.overview.data
}),
dispatch => bindActionCreators({load}, dispatch))
export default class Overview extends React.Component {
changeinput1(e) {
this.props.overview.title = e.target.value;
// changing value does not work here
}
constructor (props) {
super();
this.state = {
overview: null,
}
}
render() {
return (
<div>
<label>Input 1</label>
<input type="text" className="form-control" id="title" name="title" maxlength="35" value={this.props.overview.title} onChange={this.changeinput1.bind(this)}/>
</div>
)
}
}
I also want to do validation and want to save input value on onBlur so I dont want to use form.
if you want change reducer's(here suppose to be 'overview') value, you should define an action then dispatch it, not change it directly, the state get from store is readonly in the component