React Redux : saving and displaying data - reactjs

I am trying to make a React app where I have a form and when submitted I want to show the data on the screen. I am also using redux. I am new to all that so I can't figure out how to save the data to the store and then display it.
If someone can tell me what I need to do. I think I should save the data from the form, pass it to the store but don't know how.
import React, { Component } from "react";
import { connect } from "react-redux";
import { bindActionCreators } from "redux";
import * as actions from "../redux/actions";
export class AddAnimal extends Component {
state = {
animal: {
name: "",
animal: ""
}
};
onSubmit = e => {
console.log(e);
e.preventDefault();
};
render() {
return (
<form onSubmit={this.onSubmit}>
<input type="text" name="name" />
<br />
<input type="text" name="animal" />
<br />
<input type="submit" value="submit" className="btn" />
</form>
);
}
}
const mapStateToProps = state => {
return {
animals: state.animals
};
};
const mapStateToDispatch = dispatch => {
return bindActionCreators(
{
addAnimal: actions.addAnimal
},
dispatch
);
};
export default connect(
mapStateToProps,
mapStateToDispatch
)(AddAnimal);

You are on the right track. you can locally save the form inputs data in a state Object inside the AddAnimal component such that it won't update redux state on every key change.
And on click of onSubmit you can dispatch an action (addAnimal which can be accessed by this.props.addAnimal) with form data as an argument. On dispatching it will call your reducer and save the data in the redux state.
As your AddAnimal component is listening on animals redux's state change. On updating redux state you can fetch newly updated data via props. (this.props.animals). But this will be only required if you want to show added data on UI inside the AddAnimal component.

Unnecessary mapStateToDispatch
in the component fix export to this:
export default connect(mapStateToProps, actions)(AddAnimal);
and you do not need mapStateToDispatch
and try to active the action with: this.props.addAnimal();

Related

Passing state-based data between pages in React

I am trying to create a multiple page form in React, and I have the basic wireframe set up. I am trying to export a user's Name from one page to the next, but the user will change depending on who has logged in. I've been in google purgatory for a while trying figure out how to grab a specific state-based value out of a component to be available on another page. In my code below, I'm exporting the whole component to render on the App.js page. However, I'd also like to grab just the {userName} to render within another component.
import React, { Component } from 'react'
class Intro extends Component {
state = { userName: ''}
handleChange = (event) => this.setState({ userName: event.target.value })
render() {
const { userName } = this.state
return (
<div id='intro'>
<form>
<FieldGroup
id='nameArea'
value={this.state.value}
onChange={this.handleChange}
/>
<input id='submit' type='submit' value='Submit' /> .
</form>
</div>
)
}
}
export default Intro
To put it simply, you can't. This is where tools like redux come into play. Here's an example using React's new context API:
const UserContext = React.createContext('');
class Intro extends Component {
handleChange = (event) => {
this.props.updateUserName(event.target.value);
}
render() {
const { userName } = this.props
return (
<div id='intro'>
<form>
<input
id='nameArea'
value={userName}
onChange={this.handleChange}
/>
<input id='submit' type='submit' value='Submit' /> .
</form>
</div>
)
}
}
// only doing this to shield end-users from the
// implementation detail of "context"
const UserConsumer = UserContext.Consumer
class App extends React.Component {
state = { userName: '' }
render() {
return (
<UserContext.Provider value={this.state.userName}>
<React.Fragment>
<Intro userName={this.state.userName} updateUserName={(userName) => this.setState({userName})} />
<UserConsumer>
{user => <div>Username: {JSON.stringify(user)}</div>}
</UserConsumer>
</React.Fragment>
</UserContext.Provider>
)
}
}
See my updated codesandbox here.
Most of the time, when you need data on another component, the solution is store the date higher in your component.
As the others already said, the most easy way is trying to pass your state via props from your higher order component to the childs.
Another approach would be to use Redux for your state management. This gives you one global state store accessible from any component.
Third you can try to use the react context api.

Mapping redux state to props - state wiped out?

I am using redux, redux-form and react. I fill in the form fields, submit it and then I would like to display entered values in the Result component.
Result.jsx
import React from 'react';
import { connect } from 'react-redux';
const Result = props => {
console.log(props);
return (
<div>
test
</div>
);
};
const mapStateToProps = state => {
return {
formData: state.form
};
}
export default connect(mapStateToProps)(Result);
EDIT:
import React, { Component } from 'react';
import { BrowserRouter, Route } from 'react-router-dom';
import { Provider } from 'react-redux';
import store from './state/store';
import MainForm from './containers/MainForm';
import Result from './components/Result';
class App extends Component {
render() {
return (
<Provider store={store}>
<BrowserRouter>
<div>
<Route path="/" component={MainForm} exact />
<Route path="/submitted" component={Result} />
</div>
</BrowserRouter>
</Provider>
);
}
}
export default App;
EDIT2:
class MainForm extends Component {
state = {
submit: false
};
handleSubmit = data => {
this.setState({ submit: true });
};
render() {
const { handleSubmit, pristine, submitting, reset } = this.props;
const { submit } = this.state;
if (submit) {
return <Redirect to="/submitted" />;
}
return (
<Wrapper>
<h2>Some header</h2>
<FormWrapper onSubmit={handleSubmit(this.handleSubmit)}>
// FORM FIELDS..
<Button variant="contained" color="primary" type="submit" disabled={submitting}>
Submit
</Button>
<Button variant="contained" disabled={pristine || submitting} onClick={reset}>
Clear Values
</Button>
</FormWrapper>
</Wrapper>
);
}
}
MainForm = reduxForm({
form: 'main'
})(MainForm);
export default MainForm;
I console.log the props to check its value. It gets fired twice. The first time the props contain the form values I hoped for under props.formData.main.values. Therefore I assume the mapStateToProps worked fine. But then the console.log gets fired for the second time and props.formData is an empty Object. I also use the Redux chrome extension and I can clearly see that after form submit the data in the store are wiped out.
Could anyone explain to me this behaviour and how can I fix it? Thank you.
You're not 'consuming' submited data (dispatch action to save data in redux store?) in handler.
Probably form data are cleared after redirect for some reasons, f.e. reseting state, not holding old data for a new form on next page/url etc.
I got it fixed. The default behaviour of Redux-form is to throw away the form data once the form is unmounted. To prevent this default behaviour you have to set destroyOnUnmount to false like this:
MainForm = reduxForm({
form: 'main',
destroyOnUnmount: false // default is true
})(MainForm);
I hope this helps someone with the same problem.

Where in redux-react app would I include my stateful presentational component? In components or containers folder?

Search Component:
import React from "react";
import SearchResults from "../SearchResults";
import PropTypes from "prop-types";
class Search extends React.Component {
state = {
value: ""
};
handleChange = event => {
let value = event.target.value;
this.setState({ value });
this.props.performSearch(value);
};
handleSubmit = event => {
event.preventDefault();
};
render() {
return (
<div>
<h1>The Guardian Search App</h1>
<form onSubmit={this.handleSubmit}>
<input
type="text"
value={this.state.value}
onChange={this.handleChange}
/>
</form>
<div>
<SearchResults articles={this.props.articles} />
</div>
</div>
);
}
}
Search.propTypes = {
performSearch: PropTypes.func,
articles: PropTypes.array
};
export default Search;
Search Container:
import React from "react";
import Search from "../../components/Search";
import { API_KEY } from "../../../config";
import fetchArticles from "../../api";
class SearchContainer extends React.Component {
state = {
articles: []
};
performSearch = event => {
return fetchArticles(event).then(data =>
this.setState({ articles: data.response.results })
);
};
render() {
return (
<Search
performSearch={this.performSearch}
articles={this.state.articles}
/>
);
}
}
export default SearchContainer;
I am currently trying to get my head around redux so transitioning this into react-redux version. I've got a Search Container whereby I am doing mapStateToProps and will soon write mapDispatchToProps as well. But if my Search component also includes state, do I then do another Search Container to then map its state to props?
The state required in your Search component is directly linked and required by the input element that you have rendered as a child. Therefore, the value state in the Search component should stay within the component and not be associated with Redux.
There is no "correct" way of doing this, mainly preference and design pattern. Since you have state in the Search component that you don't want to be associated with Redux, I would hook the SearchContainer component into your Redux store for providing the array of article objects which can then be passed to the base Search component as a prop and leave that component entirely unaware that Redux even exists.

How to access redux form values in another component

I using Redux-Form 7.3.0. I am trying to get the values of my form in another component. I read the instruction at the website of Redux form but didn't work.
this is the code of my componenet:
import React from 'react'
import { Field, reduxForm } from 'redux-form';
import { connect } from 'react-redux';
import { formValueSelector } from 'redux-form';
class Test extends React.Component {
render() {
console.log(this.props);
return (
<div>
test
{this.props.title}
</div>
);
}
}
const selector = formValueSelector('NewPostForm');
Test = connect(
state => ({
title: selector(state, 'title')
})
)(Test)
export default Test;
This is my form component:
import React from 'react';
import { Field, reduxForm } from 'redux-form';
class NewPost extends React.Component {
renderField(field) {
return (
<div>
<label>{field.label}</label>
<input type="text" {...field.input} />
</div>
);
}
showResults(values) {
window.alert(`You submitted:\n\n${JSON.stringify(values, null, 2)}`);
}
render() {
const { pristine, submitting, handleSubmit } = this.props;
return (
<form onSubmit={handleSubmit(this.showResults)} >
<div>
<Field
label='Title'
name='title'
component={this.renderField}
/>
<button type='submit' disabled={submitting}>
Submit the from :)
</button>
</div>
</form>
);
}
}
export default reduxForm({ form: 'NewPostForm'})(NewPost);
but I always get
title:undefined
I found the same question here but it did not help me.
Your Test component has two imports from "redux-form". Please make it just one, like this:
import { Field, reduxForm, formValueSelector } from 'redux-form'
If your NewPost component gets unmounted at any moment, maybe by changing view or something during a navigation, the state of the form gets destroyed. You can avoid such default behavior by adding destroyOnUnmount attribute with a false value to your reduxForm wrapper:
export default reduxForm({
form: 'NewPostForm',
destroyOnUnmount: false
})(NewPost)
If this doesn't help you, please provide a better context of how you're using your components.
UPDATE: I made an example where I define 4 components:
NewPost.js: It's the form connected to the store with redux-form.
ShowPost.js: Shows what was captured (the post) by the form when you hit the submit button. This data is set to the NewPost internal state, and then it's passed as prop.
ShowPostFromSelector.js: Shows what is being captured by the form, this due to the use of the selector formValueSelector.
App.js: It's the container of the 3 components above, where the onSubmit function is defined.
Here it is: https://codesandbox.io/s/w06kn56wqk

can't type in input fields when using material-ui-redux-form due to re rendering

I'm using material-ui with redux. For some reason I can't type in my input fields whenever I follow the example provided at http://redux-form.com/6.2.0/examples/material-ui/ .
After using chrome redux dev tool I noticed that the state of the inputs is changing when I type but then it's re-rendering the entire component whenever something is typed, which makes it seem like nothing is being typed. Oddly enough, this only occurs when I use the Field component, as is used in the examples. If I just use material-ui components, the form allows typing and it doesn't re render. I've included the entire code to my component. Any help is much appreciated! What am I doing wrong?
import React, { Component } from 'react'
import {Field, reduxForm} from 'redux-form'
import { TextField } from 'redux-form-material-ui'
import RaisedButton from 'material-ui/RaisedButton'
class Login extends Component {
constructor (props) {
super(props)
this.handleFormSubmit = this.handleFormSubmit.bind(this)
}
componentDidMount () {
console.log(this.refs)
this.refs.username // the Field
.getRenderedComponent() // on Field, returns ReduxFormMaterialUITextField
.getRenderedComponent() // on ReduxFormMaterialUITextField, returns TextField
.focus() // on TextField
}
handleFormSubmit ({ username, password }) {
console.log(username, password)
}
render () {
const {
handleSubmit,
pristine,
submitting,
input,
fields: { username, password }
} = this.props
return (
<div className='loginWrapper'>
<form onSubmit={handleSubmit(this.handleFormSubmit)}>
<div id='loginNotch' />
<h1 className='loginHeader'>Login</h1>
<div>
<Field
component={TextField}
name='username'
floatingLabelText='Username'
ref='username' withRef />
</div>
<div>
<Field
component={TextField}
type='password'
name='password'
floatingLabelText='Password'
ref='password' withRef />
</div>
<div>
<RaisedButton
label='Go'
primary />
</div>
</form>
</div>
)
}
}
// TODO: keep property names consistent with server
export default reduxForm({
form: 'login',
fields: ['username', 'password']
})(Login)
Update: I took a look at the docs and removed fields from the export, and it is still not working.
You can clone project from here https://bitbucket.org/kvoth3/loanpayments.git
it's just a simple login screen
Try changing your reducer to
const rootReducer = combineReducers({
form: authReducer
})
ReduxForm expects your redux state structure to be
{
form: {
formName: {}
}
}
If you need to use a different name other than form, you need to provide a getFormState(state) to the reduxForm() decorator.

Resources