I want to create a login page. When I enter value to textInput(Email or password) on emulator, it returns INITIAL_STATE(empty string) constantly. And render() in Login.js does not re-render when I enter any input. If I change for instance email, EMAIL_CHANGED case in LoginReducer.js activated but cannot change the state. No error on debugger console. How could I solve this problem?
Main.js
render() {
const store = createStore(reducers, {}, applyMiddleware(ReduxThunk));
return (
<Provider store={store}>
<Login />
</Provider>
);}}
Login.js
render(){
const {containerStyle, subContainerStyle, inputStyle} = styles;
return(
<View style={containerStyle}>
<View style={subContainerStyle}>
<TextInput
placeholder="E-mail"
style={inputStyle}
value={this.props.email}
onChangeText={email => this.props.emailChanged(email)}
/>
</View>
<View style={subContainerStyle}>
<TextInput
secureTextEntry
placeholder="Password"
style={inputStyle}
value={this.props.password}
onChangeText={password => this.props.passwordChanged(password)}
/>
</View>
<View style={subContainerStyle}>
{this.renderLoginButton()}
</View>
</View>
);}}
const mapStateToProps = state => {
return {
email: state.auth.email,
password: state.auth.password,
loading: state.auth.loading
};
};
export default connect(mapStateToProps, { emailChanged, passwordChanged,loginWithEmail })(Login);
LoginReducer.js
import { EMAIL_CHANGED, PASSWORD_CHANGED, LOGIN_USER, LOGIN_USER_SUCCESS, LOGIN_USER_FAIL } from '../actions/types';
const INITIAL_STATE = {
email: '',
password: '',
loading: false
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case EMAIL_CHANGED:
console.log(state.email);
return { ...state, email: action.payload };
case PASSWORD_CHANGED:
return { ...state, password: action.payload };
case LOGIN_USER:
return { ...state, loading: true };
case LOGIN_USER_SUCCESS:
return { ...state, loading: false };
case LOGIN_USER_FAIL:
return { ...state, loading: false };
default:
return { ...state };
}
};
reducers/index.js
import { combineReducers } from 'redux';
import LoginReducer from './LoginReducer';
export default combineReducers({
auth: LoginReducer
});
types.js
export const EMAIL_CHANGED = 'email_changed';
export const PASSWORD_CHANGED = 'password_changed';
export const LOGIN_USER = 'login_user';
export const LOGIN_USER_SUCCESS = 'login_user_succes';
export const LOGIN_USER_FAIL = 'login_user_fail';
loginActions.js
import { EMAIL_CHANGED, PASSWORD_CHANGED, LOGIN_USER, LOGIN_USER_SUCCESS, LOGIN_USER_FAIL } from './types';
export const emailChanged = (email) => {
return (dispatch) => {
dispatch({
type: EMAIL_CHANGED,
payload: email
});
};
};
export const passwordChanged = (password) => {
return (dispatch) => {
dispatch({
type: PASSWORD_CHANGED,
payload: password
});
};
};
Your issue is that you are not passing the email and password to thunks.
const mapDispatchToProps = (dispatch) => {
return {
emailChanged: (email) => {
dispatch(emailChanged(email))
},
passwordChanged: (password) => {
dispatch(passwordChanged(password))
},
loginWithEmail: () => {
// TODO: complete this yourself, as you did not provide what it is in your code.
},
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Login);
Related
I am trying to use redux in my app for the first time and i set up whole boilerplate for it but i got an error which says Error: The slice reducer for key "AuthReducer" returned undefined during initialization. as it says i checked the state value passed in reducer but i can't figure it out, Here i am adding my code sample.
reducer.js
import {combineReducers} from 'redux'
import { SIGN_IN, SIGN_OUT, SIGN_UP, MOVIE_ADDED, MOVIE_REMOVED } from './ActionTypes'
const initialMovieState = {
savedMovies: [
{
id: '',
name: ''
}
]
}
const initialAuthState = {
userEmail: '',
userPassword: '',
userToken: '',
}
export const AuthReducer = (state=initialAuthState, action) => {
switch (action.type) {
case SIGN_IN:
return {
...state,
userToken: action.payload.token
}
case SIGN_UP:
return {
...state,
userEmail: action.payload.email,
userPassword: action.payload.password
}
case SIGN_OUT:
return {
...state,
userToken: ''
}
default:
state
}
}
export const MovieReducer = (state=initialMovieState, action) => {
switch (action.type) {
case MOVIE_ADDED:
return {
...state,
savedMovies: state.savedMovies.concat({
id: action.payload.movieId,
name: action.movieName
})
}
case MOVIE_REMOVED:
return state.filter(movie => movie.id !== action.payload.movieId)
default:
state
}
}
const RootReducer = combineReducers({
AuthReducer,
MovieReducer
})
export default RootReducer
actions.js
import {SIGN_IN, SIGN_OUT, SIGN_UP, MOVIE_ADDED, MOVIE_REMOVED} from './ActionTypes'
export const sign_in = (user) => (
{
type: SIGN_IN,
payload: {
token: user
}
}
)
export const sign_up = (user) => (
{
type: SIGN_UP,
payload: {
email: user.email,
password: user.password
}
}
)
export const sign_out = () => (
{
type: SIGN_OUT,
}
)
export const movie_added = (movie) => (
{
type: MOVIE_ADDED,
payload: {
movieId: movie.id,
movieName: movie.name,
}
}
)
export const movie_removed = () => (
{
type: MOVIE_REMOVED
}
)
store.js
import { createStore } from 'redux'
import {composeWithDevTools} from 'redux-devtools-extension'
import rootReducer from './Reducer'
const Store = createStore(rootReducer, composeWithDevTools())
export default Store
I've wrapped the whole app in Provider with store as an argument in App.js
SignUpScreen.js
import {useDispatch, useStore} from 'react-redux'
import {sign_up} from '../redux/Actions'
const SignUpScreen = () => {
const navigation = useNavigation()
const dispatch = useDispatch()
const Store = useStore()
...
const handleSubmit = () => {
if(isValidEmail === true && isValidPass === true && isPassMatched === true){
console.log(Store.getState())
dispatch(sign_up({email: email, password: password}))
console.log(Store.getState())
navigation.navigate('signin')
ToastAndroid.showWithGravity(
'Account created.',
ToastAndroid.LONG,
ToastAndroid.BOTTOM
)
} else {
ToastAndroid.showWithGravity(
'Invalid Email or Password',
ToastAndroid.LONG,
ToastAndroid.BOTTOM
)
}
}
...
return(
{/* register button */}
<TouchableOpacity style={styles.regBtnCont} onPress={() => handleSubmit()}>
<Text style={styles.regBtnText}>Register</Text>
</TouchableOpacity>
)
}
Any help will be appriciated, Thank you.
I was driven crazy by my first react-redux app. My Redux State is never updated.
I tried to find every solution on the website but they are not helping. One very similar question here used promise but I am not.
Redux-dev-tools catch the actions about the login_success but the global state is never updated, please tell me what should I do to debug if you could, thank you so much.
First Index.js
import ... from ...
const compostEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const rootReducer = combineReducers({
auth: authReducer,
})
const store = createStore(rootReducer, compostEnhancer(
applyMiddleware(thunk)
));
const app = (<BrowserRouter><App /></BrowserRouter>)
ReactDOM.render(<Provider store={store}>{app}</Provider>, document.getElementById('root'));
registerServiceWorker();
authReducer.js :
import * as actionTypes from '../actions/actionTypes';
const initialState = {
token: null,
userId: null,
error: null,
loading: false
}
const authReducer = (state = initialState, action) => {
switch (action.types) {
case actionTypes.LOGIN_START:
return {
...state,
};
case actionTypes.LOGIN_SUCCESS:
return {
...state,
token: action.idToken,
userId: action.userId,
loading: false,
error:null
}
default:
return state;
}
}
export default authReducer;
authAction.js:
import * as actionTypes from './actionTypes';
import axios from 'axios';
export const loginStart= () =>{
return {
type: actionTypes.LOGIN_START
};
}
export const loginSuccess = (token,userId) =>{
return {
type: actionTypes.LOGIN_SUCCESS,
userId: userId,
idtoken: token,
}
}
export const loginFail = (error) =>{
return {
type:actionTypes.LOGIN_FAIL,
error:error
}
}
export const auth = (email,password,isSignup ) =>{
return dispatch =>{
dispatch(loginStart());
const authData = {
email: email,
password: password,
returnSecureToken:true
}
let url = 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser?key=...'
if(!isSignup ){
url = 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key=...'
}
axios.post(url, authData)
.then( response =>{
console.log(response);
dispatch(loginSuccess(response.data.idToken, response.data.localId))
})
.catch(err=>{
console.log(err);
dispatch(loginFail(err));
})
}
}
Login.js (Component):
import ... from ...;
class Login extends Component {
constructor(props) {
super(props);
this.state = {
email: "",
password: "",
isSignup: false,
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit = e => {
e.preventDefault();
// This will trigger actions
this.props.onAuth(this.state.email, this.state.password, this.state.isSignup);
console.log(this.props.token) //here I can Get Token
}
handleChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
}
render() {
let form =
<div className={classes.ContactData} >
<h4>Sign IN</h4>
<form onSubmit={this.handleSubmit} >
<Input
elementType='input'
name="email"
required
label="Email"
placeholder="Email"
value={this.state.email}
margin="normal"
onChange={this.handleChange}
/>
<br />
<Input
elementType='input'
required
name="password"
value={this.state.password}
label="Password"
onChange={this.handleChange}
/>
<br />
<Button
color="primary"
type="submit"
>Submit</Button>
<Button color="primary" href="/"> CANCLE</Button>
</form>
</div>
if (this.props.loading) {
form = <Spinner />
}
let errorMessage = null;
if (this.props.error) {
errorMessage =(
<p>{this.props.error.message} </p>
)
}
let token = null;
if(this.props.token){
token = this.props.token.toString()
}
return (
<div>
{errorMessage}
{ form }
</div>
)
}
}
const mapStateToProps = state => {
return {
loading: state.auth.loading,
error: state.auth.error,
token:state.auth.idToken,
}
}
const mapDispatchToProps = dispatch => {
return {
onAuth: (email, password, isSignup) => dispatch(actions.auth(email, password, isSignup)),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Login);
And also, the problem leads to my Spinner and show ErrorMessage not working.
I suppose its a typo.
instead of
switch (action.types)
try this
switch (action.type)
In reducer, we get an object returned from the actions, on the argument action.
I am trying to implement the following example in my project:
https://github.com/reactjs/redux/tree/master/examples/async
But I keep running into the error: dispatch is not function
when executing the dispatch function from my actions class.
It seems as if Dispatcher is not getting passed down to my actions but I followed the flow in the example:
actions/index.js file:
export const REQUEST_LOCAL_POSTS = 'REQUEST_LOCAL_POSTS';
export const RECEIVE_LOCAL_POSTS = 'RECEIVE_LOCAL_POSTS';
export const REQUEST_CURRENT_LOCATION = 'REQUEST_CURRENT_LOCATION';
export const RECEIVE_CURRENT_LOCATION = 'RECEIVE_CURRENT_LOCATION';
export const requestCurrentLocation = () => ({
type: REQUEST_CURRENT_LOCATION
})
export const receiveCurrentLocation = currentLocation => ({
type: RECEIVE_CURRENT_LOCATION,
currentLocation,
receivedCurrentLocationAt: Date.now()
})
export const requestLocalPosts = () => ({
type: REQUEST_LOCAL_POSTS
})
export const receiveLocalPosts = json => ({
type: RECEIVE_LOCAL_POSTS,
posts: json,
receivedAt: Date.now()
})
export const fetchLocalPosts = dispatch => {
dispatch(requestCurrentLocation())
navigator.geolocation.getCurrentPosition(
(position) => {
dispatch(requestLocalPosts()) // getting dispatch is not a function here
return fetch(`http://192.168.1.3:9000/posts?lng=${position.coords.longitude}&lat=${position.coords.latitude}&radius=1000`)
.then(response => response.json())
.then(json => dispatch(receiveLocalPosts(json)))
},
(error) => this.setState({ error: error.message }),
{ enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 },
)
home.js (component):
import React, { Component } from 'react';
import {Text, View, ActivityIndicator, FlatList, Image, TouchableHighlight} from 'react-native';
import styles from './styles';
import MapView from 'react-native-maps';
import images from '../../config/images';
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { fetchLocalPosts } from '../../actions'
class Home extends Component {
static navigationOptions = ({ navigation }) => {
//const {dispatch , state, setParams} = navigation;
return {
title:<Text style={styles.title}>Localized</Text>,
headerStyle: {backgroundColor: '#2c6da4'},
headerRight: (
<TouchableHighlight onPress={() => navigation.dispatch({ type: 'MAP_EXPLORE' })}>
<Image
source={images.icons.map}
/>
</TouchableHighlight>
),
};
};
static propTypes = {
posts: PropTypes.array.isRequired,
isFetching: PropTypes.bool.isRequired,
lastUpdated: PropTypes.number,
dispatch: PropTypes.func.isRequired
}
componentDidMount() {
console.log(this.props)
const { dispatch } = this.props
dispatch(fetchLocalPosts()) // calling action from here
}
render() {
return (
<View style={styles.container}>
// i know posts isnt accessed here yet, still trying
to get past error
<FlatList
data={this.state.data}
refreshing={this.state.refreshing}
showsVerticalScrollIndicator={false}
ListHeaderComponent={this.renderHeader}
onRefresh={this._onRefresh.bind(this)}
renderItem={({item}) => <View><FlatList
data={item.posts}
horizontal={true}
snapToAlignment='center'
showsHorizontalScrollIndicator={false}
renderItem={({item}) => <Text style={styles.item}>{item.title}{"\n"}{item.snippet}</Text>}/>
<Text style={styles.subItem}>{item.Name}{"\n"}{item.Address}</Text>
</View>}
/>
</View>
);
}
}
const mapStateToProps = state => {
const { postsByBusiness } = state
const {
isFetching,
lastUpdated,
items: posts
} = postsByBusiness || {
isFetching: true,
items: []
}
return {
posts,
isFetching,
lastUpdated
}
}
export default connect(mapStateToProps)(Home)
reducer/index.js:
import { combineReducers } from 'redux';
import { NavigationActions } from 'react-navigation';
import { AppNavigator } from '../navigators/AppNavigator';
import { RECEIVE_LOCAL_POSTS, REQUEST_LOCAL_POSTS } from '../actions';
const initialNavState=AppNavigator.router.getStateForAction(NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({
routeName: 'Home',
}),
],
}));
const MAP_EXPLORE = 'MAP_EXPLORE';
const LIST_EXPLORE = 'LIST_EXPLORE';
function nav(state = initialNavState, action) {
let nextState;
switch(action.type) {
case MAP_EXPLORE:
nextState = AppNavigator.router.getStateForAction(
NavigationActions.navigate({ routeName: 'Map'}),
state
);
break;
case LIST_EXPLORE:
nextState = AppNavigator.router.getStateForAction(
NavigationActions.navigate({ routeName: 'List'}),
state
);
break;
default:
nextState = AppNavigator.router.getStateForAction(action, state);
break;
}
return nextState || state;
}
function postsByBusiness(state = { }, action) {
switch(action.type) {
case RECEIVE_LOCAL_POSTS:
return {
...state,
isFetching: false,
didInvalidate: false,
items: action.posts,
lastUpdated: action.receivedAts
}
default:
return state
}
}
const AppReducer = combineReducers({
nav,
postsByBusiness
});
export default AppReducer;
fetchLocalPosts should be returning a function that takes dispatch as an argument. Changing it to the following should fix it.
export const fetchLocalPosts = () => dispatch => {
...
It's another way of doing this:
export const fetchLocalPosts = () => {
return function (dispatch) {
...
I'm developing a mobile application by use react-native and redux,thunk and it's the first time I write by react-native.
My problem is I call an api and the response is valid, I want to do somethings as update UI, navigate to new screen... for do that I will need to used flag in my component to mark it.
This is login example, after user login success, i want to navigate to Home screen. for do that, i need check an flag isLoginSuccess in props on the method componentWillReceiveProps to know user have been login success or not, but i think it's not good solution.
My question is we have other way to do it without use flag.
action.js
export const LOGIN_SUCCESS = "LOGIN_SUCCESS";
export const LOGIN_FAIL = "LOGIN_FAIL";
export const LOGIN = "LOGIN";
export function login(username, password) {
console.log(username)
return {
type: LOGIN,
username: username,
password: password
}
}
export function loginSuccess(data) {
return {
type: LOGIN_SUCCESS,
loginData: data
}
}
export function loginFail(error) {
return {
type: LOGIN_FAIL,
error: error
}
}
export function doLogin(username, password) {
return (dispatch) => {
dispatch(login(username, password))
api.login(username, password)
.then(response => response.json())
.then(jsonData => {
console.log(jsonData)
dispatch(loginSuccess(jsonData))
})
.catch((error) => {
dispatch(loginFail(error))
})
}
}
reducer.js
const initialState = {
loginData:{},
isLoginDoing : false,
isLoginSuccess : false,
username :"",
password : "",
error : {},
}
export default function(state = initialState , action ={}){
switch(action.type){
case actionType.LOGIN:{
return {
...state,
username: action.username,
password: action.password,
isLoginDoing : true
}
}
case actionType.LOGIN_SUCCESS:{
return {
...state,
loginData: action.loginData,
isLoginDoing : false,
isLoginSuccess : true
}
}
case actionType.LOGIN_FAIL:{
return {
...state,
isLoginDoing : false,
isLoginSuccess : false,
error : action.error
}
}
default :{
return state
}
}
}
component.js
import { connect } from "react-redux"
import { bindActionCreators } from 'redux';
import { doLogin } from '../actions'
import BaseComponent from './baseComponent'
class Login extends BaseComponent {
constructor(props) {
super(props)
this.state = {
username: '',
password: '',
}
this.functionLogin = this.functionLogin.bind(this);
}
functionLogin() {
const { username, password } = this.state;
if(!this.props.loginReducer.isLoginDoing){
this.props.doLogin(username, password)
}
}
componentWillReceiveProps (nextProps) {
console.log("componentWillReceiveProps");
const { navigate, goBack } = this.props.navigation;
if(nextProps.loginReducer.isLoginSuccess){
// this._navigateTo('Home')
navigate('Home',nextProps.loginReducer.loginData);
}
}
render() {
const { navigate, goBack } = this.props.navigation;
return (
<View style={{ backgroundColor: 'color', marginTop: 10 }} >
<TextInput
style={{ height: 40 }}
placeholder="Username"
onChangeText={value => this.setState({ username: value })}
/>
<TextInput
style={{ height: 40 }}
placeholder="Password"
onChangeText={value => this.setState({ password: value })}
/>
<Button
onPress={this.functionLogin}
title="Login"
color="#841584"
/>
</View>
);
}
}
const mapStateToProps = (state) => {
console.log(state);
return {
loginReducer: state.loginReducer
};
}
function mapDispatchToProps(dispatch) {
return {
doLogin: (username, password) => dispatch(doLogin(username, password))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Login)
Thanks
In your function doLogin, you can dispatch a navigation action after dispatch(loginSuccess(jsonData)).
For example for react-navigation (if you have integrated it with redux, if it's not the case, see https://reactnavigation.org/docs/guides/redux):
dispatch(NavigationActions.navigate({routeName: 'Home'});
(Don't forget import { NavigationActions } from 'react-navigation';)
all I am fairly new to React, and Redux and have been stuck with this issue for an entire day now. The data is dispatching from my component to my action creator, then to my reducer, and the state is being updated. However, when I change inputs and begin typing, it clears all other data in the form except for the data of the input I am currently typing in. If I take out the spread operator, the data then stays, but from every tutorial, I have seen this should not happen. Am I doing something wrong?
AddProject.js (form component)
import React, { useEffect } from "react";
import styles from "./AddProjects.module.css";
import { connect } from "react-redux";
import {
validateProjectId,
validateProjectDescription,
validateProjectName,
projectStartDate,
projectEndDate,
submitHandler
} from "../../Redux/createProject/action";
const AddProject = props => {
// useEffect(() => {
// console.log("aaa", props);
// }, [props]);
return (
<div className={styles.addProjectContainer}>
<h5>Create / Edit Project form</h5>
<hr />
<form>
<div>
<input
defaultValue=""
type="text"
placeholder="Project Name"
name="projectName"
style={
props.form.projectNameError
? { backgroundColor: "#F08080", opacity: "0.8" }
: { backgroundColor: "white" }
}
onChange={e => props.validateProjectName(e.target.value)}
/>
</div>
<div>
<input
type="text"
placeholder="Unique Project ID"
name="projectIdentifier"
value={props.form.projectIdentifier}
style={
props.form.projectIdentifierError
? { backgroundColor: "#F08080", opacity: "0.8" }
: { backgroundColor: "white" }
}
onChange={e => props.validateProjectId(e.target.value)}
/>
</div>
<div>
<textarea
placeholder="Project Description"
name="description"
value={props.form.description}
style={
props.form.descriptionError
? { backgroundColor: "#F08080", opacity: "0.8" }
: { backgroundColor: "white" }
}
onChange={e => props.validateProjectDescription(e.target.value)}
/>
</div>
<h6>Start Date</h6>
<div>
<input
type="date"
name="start_date"
value={props.form.start_date}
onChange={e => props.projectStartDate(e.target.value)}
/>
</div>
<h6>Estimated End Date</h6>
<div>
<input
type="date"
name="end_date"
value={props.form.end_date}
onChange={e => props.projectEndDate(e.target.value)}
/>
</div>
<button type="button" onClick={props.submitHandler}>
<span>Submit</span>
</button>
</form>
</div>
);
};
//state.form.projectName
const mapStateToProps = state => {
console.log(state.project);
return {
form: state.project
};
};
const mapDispatchToProps = dispatch => {
return {
validateProjectName: payload => dispatch(validateProjectName(payload)),
validateProjectId: payload => dispatch(validateProjectId(payload)),
validateProjectDescription: payload =>
dispatch(validateProjectDescription(payload)),
projectStartDate: payload => dispatch(projectStartDate(payload)),
projectEndDate: payload => dispatch(projectEndDate(payload)),
submitHandler: () => dispatch(submitHandler())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(AddProject);
action.js (action creator)
import {
PROJECT_NAME_CHANGE,
PROJECT_IDENTIFIER_CHANGE,
PROJECT_DESCRIPTION_CHANGE,
START_DATE_CHANGE,
END_DATE_CHANGE,
SUBMIT_HANDLER,
PROJECT_NAME_ERROR,
PROJECT_IDENTIFIER_ERROR,
PROJECT_DESCRIPTION_ERROR
} from "./constants";
export const projectNameChange = projectName => {
return {
type: PROJECT_NAME_CHANGE,
projectName
};
};
export const projectNameError = () => {
return {
type: PROJECT_NAME_ERROR
};
};
export const projectIdChange = projectIdentifier => {
return {
type: PROJECT_IDENTIFIER_CHANGE,
projectIdentifier
};
};
export const projectIdError = () => {
return {
type: PROJECT_IDENTIFIER_ERROR
};
};
export const projectDescriptionChange = description => {
return {
type: PROJECT_DESCRIPTION_CHANGE,
description
};
};
export const projectDescriptionError = () => {
return {
type: PROJECT_DESCRIPTION_ERROR
};
};
export const projectStartDate = start_date => {
return {
type: START_DATE_CHANGE,
start_date
};
};
export const projectEndDate = end_date => {
return {
type: END_DATE_CHANGE,
end_date
};
};
export const submitHandler = () => {
return {
type: SUBMIT_HANDLER
};
};
export function validateProjectName(payload) {
return (dispatch, getState) => {
if (payload.length <= 30) {
dispatch(projectNameChange(payload));
} else {
dispatch(projectNameError());
}
};
}
export function validateProjectId(payload) {
return (dispatch, getState) => {
if (payload.length < 6) {
dispatch(projectIdChange(payload));
} else {
dispatch(projectIdError());
}
};
}
export function validateProjectDescription(payload) {
return (dispatch, getState) => {
if (payload.length < 256) {
dispatch(projectDescriptionChange(payload));
} else {
dispatch(projectDescriptionError());
}
};
}
// thunk call passed project name
// validateProjectName(name){
// if(name.length>4 && ){
// dispatchEvent(setName)
// }
// else{
// dispatch(setNameError)
// }
// }
index.js (Reducer)
PROJECT_NAME_CHANGE,
PROJECT_IDENTIFIER_CHANGE,
PROJECT_DESCRIPTION_CHANGE,
START_DATE_CHANGE,
END_DATE_CHANGE,
SUBMIT_HANDLER,
PROJECT_NAME_ERROR,
PROJECT_IDENTIFIER_ERROR,
PROJECT_DESCRIPTION_ERROR
} from "./constants";
const initialState = {
projectName: "",
projectIdentifier: "",
description: "",
start_date: "",
end_date: "",
projectNameError: false,
projectIdentifierError: false,
descriptionError: false
};
const createProjectReducer = (state = initialState, action) => {
switch (action.type) {
case PROJECT_NAME_CHANGE:
// console.log("We changed project name!", state.projectName, action);
return {
...state,
projectName: action.projectName
};
case PROJECT_IDENTIFIER_CHANGE:
// console.log("We changed project id!", state, action.projectIdentifier);
return {
...state,
projectIdentifier: action.projectIdentifier,
projectIdentifierError: false
};
case PROJECT_DESCRIPTION_CHANGE:
// console.log("We changed project description", state, action.description);
return { ...state, description: action.description };
case START_DATE_CHANGE:
// console.log("We changed the start date", state, action.payload);
return { ...state, start_date: action.payload };
case END_DATE_CHANGE:
// console.log("We changed the end date", state, action.payload);
return { ...state, end_date: action.payload };
case PROJECT_NAME_ERROR:
// console.log("There was an error with the project name!", state);
return { ...state, projectNameError: true };
case PROJECT_IDENTIFIER_ERROR:
// console.log("There was an error with the project Id!", state);
return { projectIdentifierError: true };
case PROJECT_DESCRIPTION_ERROR:
// console.log("There was an error with the project description!", state);
return { ...state, descriptionError: true };
case SUBMIT_HANDLER:
console.log("We submitted yayy", state);
return initialState;
//const formData = state;
//console.log(formData);
default:
return state;
}
};
export default createProjectReducer;
constants.js
export const PROJECT_IDENTIFIER_CHANGE = "PROJECT_IDENTIFIER_CHANGE";
export const PROJECT_DESCRIPTION_CHANGE = "PROJECT_DESCRIPTION_CHANGE";
export const START_DATE_CHANGE = "START_DATE_CHANGE";
export const END_DATE_CHANGE = "END_DATE_CHANGE";
export const SUBMIT_HANDLER = "SUBMIT_HANDLER";
export const PROJECT_NAME_ERROR = "PROJECT_NAME_ERROR";
export const PROJECT_IDENTIFIER_ERROR = "PROJECT_IDENTIFIER_ERROR";
export const PROJECT_DESCRIPTION_ERROR = "PROJECT_DESCRIPTION_ERROR";
rootReducer.js
const rootReducer = (state = {}, action) => {
return {
project: createProjectReducer(state.createProject, action)
};
};
export default rootReducer;
index.js (store creator)
import ReactDOM from "react-dom";
import { createStore, compose, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import thunkMiddleware from "redux-thunk";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import rootReducer from "./Redux/rootReducer";
const composeEnhancers =
(typeof window !== "undefined" &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) ||
compose;
const store = createStore(
rootReducer,
composeEnhancers(applyMiddleware(thunkMiddleware))
);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
serviceWorker.unregister();
In redux reducer you should always return the entire new version of the state, not just the updated parameter
exemple
return Object.assign({},prevState,{field: action.value});
(We don't see your reducer in your post but I guess that this is the problem)
Good documentation here - https://redux.js.org/basics/reducers/
Using redux to manage the state of a component is very bad practice. What you should do instead is to use useState to save the state of each of your inputs and control them with the onChange
Redux should be used ONLY for variables which are UI related and which have to be transported between multiple components all around the website.
https://reactjs.org/docs/hooks-state.html for more information
You need to know that everytime you update your redux store, the store is first copied (fully) then the new values are added and then the current store is replaced by the new one. Which drives to a lot of performance issues and memory leak.
The error is in your store creation
project: createProjectReducer(state.createProject, action)
should be
project: createProjectReducer(state.project, action)
The state is lost by not being passed to the sub reducer