currently i have one problem in my forms i cant clear inputs after form is submitted..
I have two files for input and form:
form-hook.js
import { useCallback, useReducer } from 'react';
const formReducer = (state, action) => {
switch (action.type) {
case 'INPUT_CHANGE':
let formIsValid = true;
for (const inputId in state.inputs) {
if (!state.inputs[inputId]) {
continue;
}
if (inputId === action.inputId) {
formIsValid = formIsValid && action.isValid;
} else {
formIsValid = formIsValid && state.inputs[inputId].isValid;
}
}
return {
...state,
inputs: {
...state.inputs,
[action.inputId]: { value: action.value, isValid: action.isValid }
},
isValid: formIsValid
};
case 'SET_DATA':
return {
inputs: action.inputs,
isValid: action.formIsValid
};
default:
return state;
}
};
export const useForm = (initialInputs, initialFormValidity) => {
const [formState, dispatch] = useReducer(formReducer, {
inputs: initialInputs,
isValid: initialFormValidity
});
const inputHandler = useCallback((id, value, isValid) => {
dispatch({
type: 'INPUT_CHANGE',
value: value,
isValid: isValid,
inputId: id
});
}, []);
const setFormData = useCallback((inputData, formValidity) => {
dispatch({
type: 'SET_DATA',
inputs: inputData,
formIsValid: formValidity
});
}, []);
return [formState, inputHandler, setFormData];
};
And here i got one Input.js file:
import React, { useReducer, useEffect } from 'react';
import { validate } from '../../util/validators';
const inputReducer = (state, action) => {
switch (action.type) {
case 'CHANGE':
return {
...state,
value: action.val,
isValid: validate(action.val, action.validators)
};
case 'TOUCH': {
return {
...state,
isTouched: true
};
}
default:
return state;
}
};
const Input = props => {
const [inputState, dispatch] = useReducer(inputReducer, {
value: props.initialValue || '',
isTouched: false,
isValid: props.initialValid || false
});
const { id, onInput, clearInputs } = props;
const { value, isValid } = inputState;
useEffect(() => {
onInput(id, value, isValid);
}, [id, value, isValid, onInput]);
const changeHandler = event => {
dispatch({
type: 'CHANGE',
val: event.target.value,
validators: props.validators
});
};
const touchHandler = () => {
dispatch({
type: 'TOUCH'
});
};
const element =
props.element === 'input' ? (
<input
id={props.id}
type={props.type}
placeholder={props.placeholder}
onChange={changeHandler}
onBlur={touchHandler}
value={inputState.value}
maxLength={props.maxlength}
/>
) : (
<textarea
id={props.id}
rows={props.rows || 3}
onChange={changeHandler}
onBlur={touchHandler}
value={inputState.value}
/>
);
return (
<div
className={`form-control ${!inputState.isValid &&
inputState.isTouched &&
'form-control--invalid'}`}
>
<label htmlFor={props.id}>{props.label}</label>
{element}
{!inputState.isValid && inputState.isTouched && <p>{props.errorText}</p>}
</div>
);
};
export default Input;
My problem here is that i can't access this inputState, it looks like the value is stored in it...
And this is just an example how is this used in my components and this is functional programing in reactjs..
import React, { useState, useContext } from 'react';
import { Text, LanguageContext } from '../../lang/containers/Language';
import Input from '../../shared/components/FormElements/Input';
import Button from '../../shared/components/FormElements/Button';
import ErrorModal from '../../shared/components/UIElements/ErrorModal';
import {
VALIDATOR_EMAIL
} from '../../shared/util/validators';
import { useForm } from '../../shared/hooks/form-hook';
import { useHttpClient } from '../../shared/hooks/http-hook';
const NewSletter = () => {
const { dictionary } = useContext(LanguageContext);
const { isLoading, error, sendRequest, clearError } = useHttpClient();
const [joined, setJoined] = useState(false);
const [formState, inputHandler] = useForm(
{
email: {
value: '',
isValid: false
}
},
false
);
const newsletterSubmit = async event => {
event.preventDefault();
try {
const responseData = await sendRequest(
'http://localhost:5000/api/users/newsletter',
'POST',
JSON.stringify({
email: formState.inputs.email.value,
}),
{
'Content-Type': 'application/json'
}
);
if(responseData.message == 'subscribed'){
setJoined(true);
//here i need to clean all inputs in this form
}
} catch (err) {
console.log(err)
}
};
return (
<>
<ErrorModal error={error ? <Text tid={error}/> : null} onClear={clearError} />
<div className="newsletter-bg">
<div className="container">
<div className="newsletter-bg-title"><Text tid="newsletter"/></div>
<div className="space10px"></div>
<form className="newsletter-form" onSubmit={newsletterSubmit}>
<Input
element="input"
id="email"
type="email"
label={<Text tid="auth_lang4" />}
validators={[VALIDATOR_EMAIL()]}
errorText={<Text tid="auth_lang5" />}
onInput={inputHandler}
/>
<Button type="submit" disabled={!formState.isValid}>
<Text tid="add"/>
</Button>
{joined === true ? <div className="biltenmsg"> <span style={{color: 'green'}}><Text tid="joinedsletter"/></span> </div> : null}
</form>
</div>
</div>
</>
);
};
export default NewSletter;
I also tried serveral examples from here like reset() and others things, also tried to change useForm things but i couldnt delete value because it read value from input.js file in inputState how i figured... I also tryed with setformdata and some others examples but i didnt solve this problem.. Best regards.
You can only update inputState by dispatch only
const inputReducer = (state, action) => {
switch (action.type) {
case 'CHANGE':
return {
...state,
value: action.val,
isValid: validate(action.val, action.validators)
};
case 'TOUCH': {
return {
...state,
isTouched: true
};
}
case 'RESET': {
return {
value: action.value,
isTouched: action.isTouched,
isValid: action.isValid
};
}
default:
return state;
}
};
then
dispatch({
type: 'RESET',
value: props.initialValue || '',
isTouched: false,
isValid: props.initialValid || false
});
I don't see much problem in your formReducer which got used by useForm and inside you defined a inputHandler that is must-have to get any input work. However your Input is overly complicated IMHO.
Attached is an example useForm
const useForm = (props = {}) => {
const [state, dispatch] = useReducer(reducer, initialState(props))
const change = useCallback((payload) => {
dispatch({ type: 'change', payload })
}, [])
const validate = useCallback((payload) => {
if (!state.validate) return
dispatch({ type: 'validate', payload: payload || state.values })
}, [state])
const submit = useCallback((payload) => {
dispatch({ type: 'submit', payload })
}, [])
const onChange = useCallback(e => {
const target = e.target
const payload = { [target.name]: target.value }
change(payload)
validate(payload)
}, [change, validate])
const onSubmit = useCallback(e => {
if (e) e.preventDefault()
submit(state.values)
}, [submit, state.values])
/**
* #typedef {object} useFormObject
* #property {string} name Form name
* #property {object} values Form values
* #property {func} change Form change function
* #property {object} handlers Form events
* #property {func} handlers.onChange Event upon input change
* #property {func} handlers.onSubmit Event upon form submit
* #property {func} validate Form validate function
* #property {object} errors Form errors
* #property {bool} error Form in error
*/
return {
name: state.name,
values: state.values,
change,
handlers: {
onChange,
onSubmit
},
validate,
errors: state.errors,
error: hasError(state.errors),
}
}
export default useForm
and the usage is
const { values, handlers, errors } = useForm({ initialValues })
and then every input widget is about
<input value={values.abc} onChange={handlers.onChange} />
You can see there's no need for another level of reducer inside input, because both value and onChange are almost "read-only" properties from input point of view.
For completeness, reducer is attached here.
import hasError from './hasError'
const defaultState = {
values: {},
errors: {},
watchers: {}
}
const reducer = (state = defaultState, action) => {
let payload = {}
if (action && action.payload) {
payload = action.payload
}
const watchers = state.watchers || {}
const watch = state.watch || (() => { })
const validate = state.validate || (() => { })
const { onSubmitted } = watchers
let errors
switch (action.type) {
case 'change':
watch(action.type, payload)
return {
...state,
values: {
...state.values,
...payload
}
}
case 'validate':
errors = validate(payload, action.type)
watch(action.type, errors)
return {
...state,
errors: {
...state.errors,
...errors
}
}
case 'submit':
errors = validate(payload, action.type)
watch('validation', errors)
if (hasError(errors)) {
return {
...state,
errors
}
}
if (onSubmitted) {
onSubmitted(state.values)
}
return state
default:
return state
}
}
export default reducer
Related
could you please help with setting state in useContext ?
I am trying to send video variable through useEffect to setMediaContent to update mediaContext.media object. My goal is to have several media(video,images,posters) objects in media object, e.g.
https://codesandbox.io/s/friendly-sunset-o67nvj?file=/src/context.js
Thanks in advance
Try using a reducer:
import { createContext, useReducer } from "react";
// default state
const contextDefaultValues = {
video: { url: "", title: "", shown: false },
openVideo: () => {},
closeVideo: () => {},
mediaContent: { media: {}, title: "most" },
setMediaContent: () => {},
};
const MainReducer = (state = contextDefaultValues, action) => {
const { type, payload } = action;
switch (type) {
case "setMediaContent": {
const { media, title } = payload;
return { ...state, media: { ...state.media, ...media }, title: title };
}
case "closeVideo": {
return { ...state, shown: false };
}
case "openVideo": {
const { url, title } = payload;
return { ...state, url, title, shown: true };
}
default: {
throw new Error(`Unhandled action type: ${type}`);
}
}
};
export const MainContext = createContext(contextDefaultValues);
// provider recuder
const MainProvider = ({ children }) => {
const [state, dispatch] = useReducer(MainReducer, contextDefaultValues);
const openVideo = (url, title) => {
dispatch({ type: "openVideo", payload: { url, title, shown: true } });
};
const closeVideo = () => {
dispatch({ type: "closeVideo", payload: { shown: false } });
};
const setMediaContent = (media, title) => {
dispatch({ type: "setMediaContent", payload: { media, title } });
};
return (
<MainContext.Provider
value={{ ...state, setMediaContent, closeVideo, openVideo }}
>
{children}
</MainContext.Provider>
);
};
export default MainProvider;
Based on the provided sandbox, You have the render of the provider wrapped in the setMediaContent function.
Look at the { and } at line 36 and 58.
Code screenshot with misplaced brackets
I have build this generic dropdown/select component with an async function to get datasets. For some reason I get the message The 'fetchData' function makes the dependencies of useEffect Hook (at line 48) change on every render. To fix this, wrap the definition of 'fetchData' in its own useCallback() Hook.eslintreact-hooks/exhaustive-deps.
I don't change any value of a dependency from my useEffect hook since these properties are controlled by my redux slice....
Select component:
import React, { useEffect } from 'react';
import { Form, Select, Typography } from 'antd';
import PropTypes from 'prop-types';
import styled from 'styled-components';
const StyledSelect = styled(Select)`
&.ant-select-loading .ant-select-selection-item {
display: none;
}
`;
const { Text } = Typography;
const CustomSelect = ({
endpointKey,
dataKey,
customLabel,
required = false,
dataSet,
fetchDataSet,
disabled = false,
fullOptionHeight = false,
onChange = null,
showSearch = false,
setLoading = null,
}) => {
const fetchData = async (searchText) => {
if (setLoading) {
setLoading(true);
}
await fetchDataSet({ endpointKey, searchText });
if (setLoading) {
setLoading(false);
}
};
useEffect(() => {
const dataSetPresent = !!dataSet.data && !!Object.keys(dataSet.data).length;
const hasError = dataSet.errorMessage && dataSet.errorMessage.length;
if (
!dataSetPresent &&
dataSet.loadingStatus !== 'loading' &&
dataSet.loadingStatus !== 'loaded' &&
!hasError
) {
fetchData();
}
}, [fetchData, dataSet]);
const { loadingStatus, data, errorMessage } = dataSet;
const label = customLabel || endpointKey;
const formErrorMessage = 'Please select ' + label.toLowerCase();
const placeholder = `-- Select a ${label.toLowerCase()} --`;
const renderSelect = () => {
if (errorMessage !== '') {
return <Text type="danger">{errorMessage}</Text>;
}
return (
<StyledSelect
disabled={loadingStatus === 'loading' || disabled}
loading={loadingStatus === 'loading'}
placeholder={placeholder}
optionFilterProp="children"
style={{ maxWidth: '500px' }}
size="large"
onChange={onChange}
showSearch={showSearch}
onSearch={(value) => {
if (showSearch) {
fetchData(value);
}
}}
>
{Object.keys(data).map((dataObject) => {
return (
<Select.Option
className={`${fullOptionHeight ? 'full-option-height' : ''}`}
value={dataObject}
key={dataObject}
>
{data[dataObject]}
</Select.Option>
);
})}
</StyledSelect>
);
};
return (
<Form.Item
label={label}
name={dataKey}
rules={[{ required, message: formErrorMessage }]}
>
{renderSelect()}
</Form.Item>
);
};
CustomSelect.propTypes = {
endpointKey: PropTypes.string.isRequired,
dataKey: PropTypes.oneOfType([PropTypes.array, PropTypes.string]),
customLabel: PropTypes.string,
required: PropTypes.bool,
fetchDataSet: PropTypes.func,
showSearch: PropTypes.bool,
dataSet: PropTypes.object,
disabled: PropTypes.bool,
fullOptionHeight: PropTypes.bool,
onChange: PropTypes.func,
setLoading: PropTypes.func,
};
export default CustomSelect;
React Slice with the async hook and state change handling:
import { createAsyncThunk, createSlice } from '#reduxjs/toolkit';
import callApi from 'utils/api';
export const SELECT_KEY = 'select';
export const fetchDataSet = createAsyncThunk(
'select/request-data',
async ({ endpointKey, searchText }) => {
const endpoint = `data/${endpointKey}`;
try {
const { data } = await callApi({
endpoint,
});
return data;
} catch (error) {
console.error('ERROR', error);
throw error;
}
}
);
export const selectSlice = createSlice({
name: SELECT_KEY,
initialState: {},
reducers: {},
extraReducers: {
[fetchDataSet.pending]: (state, action) => {
const key = action.meta.arg.endpointKey;
return {
...state,
[key]: {
loadingStatus: 'loading',
errorMessage: '',
data: {},
},
};
},
[fetchDataSet.fulfilled]: (state, action) => {
const key = action.meta.arg.endpointKey;
return {
...state,
[key]: {
loadingStatus: 'loaded',
errorMessage: '',
data: action.payload,
},
};
},
[fetchDataSet.rejected]: (state, action) => {
const key = action.meta.arg.endpointKey;
return {
...state,
[key]: {
loadingStatus: 'error',
errorMessage: action.error.message,
data: {},
},
};
},
},
});
export const selectReducer = selectSlice.reducer;
export const dataSetSelector = (state, key) => state.select[key] || {};
You do actually change a value of a dependency each render cycle since you redeclare fetchData each cycle. The warning is suggesting you simply memoize this callback function so you provide a stable reference for the effect hook.
const fetchData = useCallback(async (searchText) => {
if (setLoading) {
setLoading(true);
}
await fetchDataSet({ endpointKey, searchText });
if (setLoading) {
setLoading(false);
}
}, [endpointKey]); // <-- add any other necessary dependencies for the callback
I have wriiten the below code in which the city the alert function initially works fine when a wrong city name or no city name is entered. But after the Weather details are displayed here again when I click on submit then it re renders the previous state and new one and gives both result.
Code:
import React, { FC, useState, FormEvent } from "react";
import { useDispatch } from "react-redux";
import { Header, Input, Button } from "../style";
import {
getWeather,
setLoading
} from "../../store/actions/WeatherAction/weatherActions";
import { setAlert } from "../../store/actions/AlertAction/alertActions";
interface SearchProps {
title: string;
}
const Search: FC<SearchProps> = ({ title }) => {
const dispatch = useDispatch();
const [city, setCity] = useState("");
const changeHandler = (e: FormEvent<HTMLInputElement>) => {
setCity(e.currentTarget.value);
};
const submitHandler = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
dispatch(setLoading());
dispatch(getWeather(city));
setCity("");
};
return (
<>
<Header>
{title}
<form onSubmit={submitHandler}>
<Input
type="text"
placeholder="Enter city name"
value={city}
onChange={changeHandler}
/>
<br />
<Button>Search</Button>
</form>
</Header>
</>
);
};
export default Search;
weatherAction.ts
import { ThunkAction } from "redux-thunk";
import { RootState } from "../..";
import {
WeatherAction,
WeatherData,
WeatherError,
GET_WEATHER,
SET_LOADING,
SET_ERROR
} from "../../types";
export const getWeather = (
city: string
): ThunkAction<void, RootState, null, WeatherAction> => {
return async (dispatch) => {
try {
const res = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=3020950b62d2fb178d82816bad24dc76`
);
if (!res.ok) {
const resData: WeatherError = await res.json();
throw new Error(resData.message);
}
const resData: WeatherData = await res.json();
dispatch({
type: GET_WEATHER,
payload: resData
});
} catch (err) {
dispatch({
type: SET_ERROR,
payload: err.message
});
}
};
};
export const setLoading = (): WeatherAction => {
return {
type: SET_LOADING
};
};
export const setError = (): WeatherAction => {
return {
type: SET_ERROR,
payload: ""
};
};
weatherReducer
import {
WeatherState,
WeatherAction,
GET_WEATHER,
SET_LOADING,
SET_ERROR
} from "../../types";
const initialState: WeatherState = {
data: null,
loading: false,
error: ""
};
export default (state = initialState, action: WeatherAction): WeatherState => {
switch (action.type) {
case GET_WEATHER:
return {
data: action.payload,
loading: false,
error: ""
};
case SET_LOADING:
return {
...state,
loading: true
};
case SET_ERROR:
return {
...state,
error: action.payload,
loading: false
};
default:
return state;
}
};
The problem is that your reducer does not clear the weather data when processing a SET_ERROR action. If you want to clear the weather data when you receive an error, you should set data back to null like this:
case SET_ERROR:
return {
data: null,
error: action.payload,
loading: false
};
I wonder what I am doing wrong here. The dispatch methods are dispatching correct values but the state object is showing wrong values.
{ name: "name", room: "room" } is what I am dispatching separately. But the state is showing { name: "room": room: "" }
Google chrome logs :
NOTE: please checkout the code here from the github repo incase needed.
Reducer:
export const initialState = {
name: '',
room: ''
}
export const reducer = (state, action) => {
console.log("Calling action", action);
switch (action.type) {
case types.SET_NAME:
return { ...state, name: action.name }
case types.SET_ROOM:
return { ...state, name: action.room }
default:
return state;
}
}
_app component:
import DataProvider from "../context/DataProvider";
import { initialState } from '../reducers/index';
import { reducer } from '../reducers';
const AppComponent = ({ Component, pageProps }) => {
return (
<DataProvider intialState={initialState} reducer={reducer}>
<Component {...pageProps} />
</DataProvider>
)
}
AppComponent.getInitialProps = async (appContext) => {
let pageProps = {};
if (appContext.Component.getInitialProps) {
pageProps = await appContext.Component.getInitialProps(appContext.ctx);
}
return { pageProps }
}
export default AppComponent;
Component:
const Join = () => {
const [name, setName] = input('');
const [room, setRoom] = input('');
const [state, dispatch] = useContext(DataContext);
const submit = (e) => {
if (name === '' || room === '') {
e.preventDefault();
return;
}
dispatch({
type: types.SET_NAME,
name
});
dispatch({
type: types.SET_ROOM,
room
});
}
return (
<div>
<h1>Join</h1>
<input onChange={(e) => setName(e)} placeholder="name" />
<input onChange={(e) => setRoom(e)} placeholder="room" />
<Link href="/chat">
<button type="submit" onClick={(e) => submit(e)}>Submit</button>
</Link>
</div>
)
}
Chat component (where I am consuming state):
const Chat = () => {
// const backendEndpoint = 'http://localhost:5000';
const [state, dispatch] = useContext(DataContext);
console.log('STATE', state)
return <h1>Chat</h1>
}
Chat.getInitialProps = async (ctx) => {
return {}
}
export default Chat;
I think the problem is in your reducer
case types.SET_ROOM:
return { ...state, name: action.room }
Here you change the name in rooms action
maybe you need to update like this
return { ...state, room: action.room }
actually u make a mistake in your Reducer.js
export const reducer = (state, action) => {
console.log("Calling action", action);
switch (action.type) {
case types.SET_NAME:
// equals state.name = action.name
// state = { name: 'name', room: '' }
return { ...state, name: action.name }
case types.SET_ROOM:
// equal state.name = action.room
// state = { name: 'room', room: '' }
return { ...state, name: action.room }
default:
return state;
}
}
// u can change your code style to reduce mistakes
export const reducer = (state, action) => {
const {name, room} = action
switch (action.type) {
case types.SET_NAME:
return { ...state, name }
case types.SET_ROOM:
return { ...state, room }
default:
return state;
}
}
I'm trying to show a list of recipes from an API in my component when a form submitted. It doesn't show any result in the component and doesn't have any error!
May somebody help me , What's wrong with my code ?
here is my action.js
import { getDataConstants } from "../_constants";
import { getDataService } from "../_service";
export const getDataAction = {
fetchRecipes
}
function fetchRecipes(query) {
return dispatch => {
dispatch(loading());
getDataService.fetchRecipes(query).then(
response => {
dispatch(success(response));
},
error =>{
dispatch(failed(error));
}
)
}
function loading() { return { type: getDataConstants.FETCH_RECIPES_LOADING }; }
function success(data) { return { type: getDataConstants.FETCH_RECIPES_SUCCESS, data }; }
function failed(error) { return { type: getDataConstants.FETCH_RECIPES_FAILED, error }; }
}
code for reducer.js
import { getDataConstants } from "../_constants";
const initialState = {
loading: false,
items: [],
error: null
};
export function getDataReducer(state = initialState, action) {
switch (action.type) {
case getDataConstants.FETCH_RECIPES_LOADING:
return {
...state,
loading: true,
error: null,
items: []
};
case getDataConstants.FETCH_RECIPES_SUCCESS:
return {
...state,
loading: false,
items: action.payload
};
case getDataConstants.FETCH_RECIPES_FAILED:
return {
...state,
loading: false,
error: action.payload,
items: []
};
default:
return state;
}
}
export const getRecipes = state => state.items;
export const getRecipesloading = state => state.loading;
export const getRecipesError = state => state.error;
I fetch data in the service.js component
code for service.js
import {TIMEOUT_DELAY,HOST} from '../_constants';
import axios from 'axios';
export const getDataService = {
fetchRecipes
}
async function fetchRecipes(query) {
let timeout = null;
try{
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
timeout = setTimeout(()=>{source.cancel()},TIMEOUT_DELAY);
debugger
const response = await axios({
url: `${HOST}?apiKey=94be430aadf644f6a8c8c95abbcce4c1&query=${query}&_number=12`,
method: "get",
headers: { "content-type": "application/json" },
cancelToken: source.token
});
if (response.status === 200) {
if (timeout) clearTimeout(timeout);
return response.data;
} else {
if (timeout) clearTimeout(timeout);
return Promise.reject({isTimeout:false,error: response.data});
}
}catch (error) {
if (timeout) clearTimeout(timeout);
if (axios.isCancel(error)) {
return Promise.reject({isTimeout:true});
} else {
return Promise.reject({isTimeout:false,error});
}
}
}
code for Recipes component where API response data shown
const Recipes = props => {
const { dispatch, error, loading, items } = props;
const classes = useStyles();
const [query, setQuery] = useState("beef");
const submitHandler = async event => {
event.preventDefault();
dispatch(getDataAction.fetchRecipes(query));
};
const handleChange = event => {
setQuery(event.target.value);
};
return (
<>
<form onSubmit={submitHandler} className={classes.formWidth}>
<input
type="text"
value={query}
onChange={handleChange}
className={classes.input}
/>
</form>
{error && <div>Something went wrong ...</div>}
{loading ? (
<div>
<img src={Loading} alt="Loading" />
</div>
) : (
<ul className={classes.centeredDiv}>
{items &&
items.results.map(recipe => (
<li
className={classes.media}
image={`${imgUrl}${recipe.image}`}
title={recipe.title}
/>
))}
</ul>
)}
}
</>
);
}
const mapStateToProps = state => {
return {
loading: getRecipesloading(state),
items: getRecipes(state),
error: getRecipesError(state)
};
};
Sorry about the large amount of code dumped, its just all related and I believe the error lies somewhere.
Your reducer expects action.payload but instead you send action.data. It should be:
case getDataConstants.FETCH_RECIPES_SUCCESS:
return {
...state,
loading: false,
items: action.data
}
You should update your success and failed functions like so:
function success(data) {
return {
type: getDataConstants.FETCH_RECIPES_SUCCESS,
payload: data
};
}
function failed(error) {
return {
type: getDataConstants.FETCH_RECIPES_FAILED,
payload: error
};
}
To avoid such typo problems you can create default action creator:
function createAction(type, payload, meta) {
return {
type: type,
payload: payload,
meta: meta
};
}
// usage
function success(data) {
return createAction(getDataConstants.FETCH_RECIPES_SUCCESS, data)
}