How do I pass state into my action creator - javascript

I wanted to know how I can pass the state into an action so that it can use the state to make an API call. the state I want to pass is the input because it's the image URL that gets sent to Clarifai's servers to predict the celebrity. The handle search is responsible for updating state to the URL.
I've tried to use get state with no luck
This is my action
export const requestPrediction = () => {
return function (dispatch, getState) {
dispatch(fetchCelebrequest)
let input = getState().input
app.models.predict(Clarifai.CELEBRITY_MODEL,
input)
.then(res => {
const data = res.outputs[0]['data']['regions'][0]['data'].concepts[0]
dispatch(fetchCelebSuccess(data))
})
.catch(err => {
const error = err.message
dispatch(fetchCelebFailed(error))
})
}
}
This is my reducer.js
import {
CHANGE_SEARCHFIELD,
REQUEST_PREDICTION_PENDING,
REQUEST_PREDICTION_SUCESS,
REQUEST_PREDICTION_FAILED
} from './constants'
const initialState = {
input: '',
imageUrl: '',
box: {},
isSignedIn: false,
isPending: false,
celeb: {},
error: '',
celebConfidence: [],
user: {
id: '',
name: '',
email: '',
entries: 0,
joined: ''
}
}
export const handleSearch = (state=initialState, action={}) => {
switch (action.type) {
case CHANGE_SEARCHFIELD:
return { ...state, input: action.payload }
default:
return state
}
}
export const requestPrediction = (state=initialState, action={}) => {
switch(action.type) {
case REQUEST_PREDICTION_PENDING:
return {...state, isPending: true}
case REQUEST_PREDICTION_SUCESS:
return {...state, celebName: action.payload, isPending: false}
case REQUEST_PREDICTION_FAILED:
return {...state, error: action.payload, isPending: false}
default:
return state
}
}

The proper way to do it is via setState since the updates my be asynchronous.
You may check out this link. How can I pass state to an action in React.js?

Related

How to use reducer in redux in react

I am using Redux for state management, I have faced an issue in reducer function
here is the image of my console, You can see the Product Action is providing my data but the reducer is not passing on my function
here is my code of ProductAction:
export const getProductsbyFind = (myvariable) =>async (dispatch)=>{
try {
console.log(myvariable)
dispatch({type: ALL_PRODUCTS_REQUEST_BY_ID})
const{ data } = await axios.get(`/api/v1/product/${myvariable}`)
console.log(data)
dispatch({
type: ALL_PRODUCTS_SUCCESS_BY_ID,
payload: data
})
} catch (error) {
dispatch({
type:ALL_PRODUCTS_FAIL,
payload: error.response.data.message
})
}
}
here is the code of Reducer:
export const productReducersById = (state = { products: [] }, action) => {
switch (action.type) {
case ALL_PRODUCTS_REQUEST_BY_ID:
return {
loading: true,
products: []
}
case ALL_PRODUCTS_SUCCESS_BY_ID:
return {
loading: false,
products: action.payload.products,
productsCount: action.payload.productsCount
}
case UPDATE_QUANTITY_BY_ID:
const { index, quantity } = action.payload;
const prods = state.products.map((p, i) => {
if (i !== index)
return p;
return {
...p,
quantity
}
});
return {
loading: true,
products: prods
}
case ALL_PRODUCTS_FAIL_BY_ID:
return {
loading: false,
error: action.payload
}
case CLEAR_ERRORS_BY_ID:
return {
...state,
error: null
}
default:
return state
}
}
here is the code of my page where I want to get my data:
const { loading, products, error, productCount } = useSelector(state => state.products);
console.log(products)
useEffect(() => {
dispatch(getProductsbyFind(myvariable));
}, [dispatch])
You have a typo in your reducer:
case ALL_PRODUCTS_SUCCESS_BY_ID:
return {
loading: false,
- products: action.payload.products,
+ products: action.payload.product,
productsCount: action.payload.productsCount
}
(Also, productsCount does not exist in your payload, so that will become undefined.)

react redux-thunk wraps state inside another state

Im new to react, now Im creating a simple app using redux and redux-thunk which calls an API asynchronously. Here is my game gameAction:
export const fetchGamesStartAsync = () => {
return dispatch => {
dispatch(fetchGamesStart());
axiosGenCfg.post('/game/get',{
"page" : 1,
"size" : 10
})
.then(({ res }) => {
dispatch(fetchGamesSuccess(res));
})
.catch(err => {
dispatch(fetchGamesFailure(err.message));
});
}
};
const fetchGamesStart = () => ({
type: gameActionTypes.FETCH_GAMES_START,
});
const fetchGamesFailure = () => ({
type: gameActionTypes.FETCH_GAMES_FAILURE,
});
const fetchGamesSuccess = (games) => ({
type: gameActionTypes.FETCH_GAMES_SUCCESS,
payload:{
...games
}
});
and this is my gameReducer:
const INITIAL_STATE= {
gamesList : null,
isFetching: false,
errorMessage : undefined
};
const gameReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case gameActionTypes.FETCH_GAMES_START:
return{
...state,
isFetching: true
};
case gameActionTypes.FETCH_GAMES_SUCCESS:
return{
...state,
isFetching: false,
gamesList: action.payload
};
case gameActionTypes.FETCH_GAMES_FAILURE:
return{
...state,
isFetching: false,
errorMessage: action.payload
};
default:
return {
state
};
}
};
and in rootReducer
export default combineReducers({
admin : adminReducer,
game: gameReducer,
})
I also added redux-logger to check state and this is what i get in console
So why there are 2 levels of state in my game object? and also the same with admin object. before i add redux-thunk to project, I didn't have this problem. before adding redux-thunk currentAdmin was direct child of admin. But now there is a state object between.
default:
return {
state
};
should just be
default:
return state
Right now any time you hit the default, state.whatever is becoming state.state.whatever

TypeError: reviews.reviewList.data is undefined in reactjs even when data is there

I am getting the response from backed which looks something like this.
But when i try to log the data like this.
render() {
const { reviews } = this.props;
console.log('rev', reviews.reviewList.data._embedded);
It gives me error saying this.
TypeError: reviews.reviewList.data is undefined
reviewDataReducer.jsx
const initialstate = {
isFetching: false,
reviewList: [],
page: null,
fetched: false,
error: null
};
export default (state = initialstate, action) => {
switch (action.type) {
case actionTypes.GET_PRODUCT_REVIEWS_LOAD:
return {
...state,
isFetching: true
};
case actionTypes.GET_PRODUCT_REVIEWS_SUCCESS:
return {
...state,
fetched: true,
reviewList: action.payload
};
case actionTypes.GET_PRODUCT_REVIEWS_ERROR:
return {
...state,
fetched: false,
isFetching: false,
error: action.error
};
default:
return state;
}
};
reviewActions.jsx
export const getProductReviews = pid => dispatch => {
console.log('rev pid',pid)
dispatch({
type: types.GET_PRODUCT_REVIEWS_LOAD
});
new _rest()
.get(`/buyer/product/${pid}/review`)
.then(res => {
console.log("Review Action Response", res);
dispatch({
type: types.GET_PRODUCT_REVIEWS_SUCCESS,
payload: res
});
})
.catch(error => {
dispatch({
type: types.GET_PRODUCT_REVIEWS_ERROR,
error: error
});
});
};
connect
const mapStateToprops = state => ({
reviews: state.reviews.data
});
const mapStateToDispatch = {
getProductReviews
};
export default connect(
mapStateToprops,
mapStateToDispatch
)(ReviewContainer);
Your information is limited but I will try to be best.
1] Error is because you'r traversing object wrong not because data in not there in this case.
2] render() {
const { reviews } = this.props;
Here I feel you mapping redux state to prop using (mapStateToProps) if so reducer is responsible how you set data in redux state.
The issue is there inside the mapStateToprops connection. Try to debug it there.
const mapStateToprops = state => {
debugger;
return ({
reviews: state.reviews.data
});
};
Open your browser console and check the value of state;

using reducer namespace in redux to practice DRY

I want to add a loader for each action, like the buttons will display loading when dispatch (see demo)
export default class Items extends Component {
render() {
return (
<div>
<div>status: {this.props.item.status}</div>
<button onClick={() => this.props.resetItem()}>
{this.props.loading ? "loading..." : "Reset"}
</button>
<button onClick={() => this.props.approveItem()}>
{this.props.loading ? "loading..." : "Approve"}
</button>
</div>
);
}
}
The problem is all button will show loading because my reducer has a global loading state only
export function items(state = initState, action) {
switch (action.type) {
case "APPROVE":
return {
...state,
loading: true
};
case "APPROVED":
return {
...state,
loading: false,
item: {
status: "approved"
}
};
case "RESET":
return {
...state,
loading: true
};
case "DONE_RESET":
return {
...state,
loading: false,
item: {
status: "pending"
}
};
default:
return state;
}
}
I can hardcode approve_loading, reset_loading and so on but that's redundancy, any technique to do namespacing in reducer?
Neat question - have never run into this myself but I'm wondering if something like this could work. You can use combineReducers() to namespace, so a perhaps not entirely elegant approach could be:
export function itemsReducer(index) {
return function items(state = {}, action) {
switch (action.type) {
case `APPROVE_${index}`:
return {
...state,
loading: true,
};
case `APPROVED_${index}`:
return {
...state,
loading: false,
item: {
status: 'approved',
},
};
default:
return state;
}
};
}
const reducers = {};
//could add more indexes here for more items;
[0, 1].forEach(i => {
reducers[`item${i}`] = itemsReducer(i);
});
export default combineReducers(reducers);
//state = {
// item0: {...},
// item1: {...}
//}
Your actions would then need to include the appropriate index (0 or 1) when dispatching (e.g. APPROVED_1) so the correct item state will get set.
Equivalent syntax:
export default combineReducers({
item0: itemsReducer(0),
item1: itemsReducer(1)
});

Redux state - Add/Edit/Remove object and its properties

This is the reducer state. I need to add, update, remove the object in cartData. At the first time, cartData is empty.
const initialState = {
fetchData: {},
cartData: {}
}
Example:
fetchData: {
"React":{'name': 'React'},
"Node":{'name': 'Node'},
}
If user ADD_ITEM react book, new item is adding in the cart here.
cartData:{
"React":{'name': 'React', 'quantity': 1},
}
If user Edit_ITEM react book, existing item is updating here.
cartData:{
"React":{'name': 'React', 'quantity': 4},
}
If user REMOVE_ITEM react book, removing when its come to zero here.
cartData:{
}
How can we modify redux state for these actions?
Tried this: using lodash. But did't worked out correctly.
case types.ADD_ITEM:
return { ...state, cartData: // add new item }
case types.EDIT_ITEM:
return { ...state, [state.cartData.name]: action.payload }
case types.REMOVE_ITEM:
return _.omit(state, [state.cartData.name]: action.payload)
You can use spread syntax for add and edit items and Object.keys() and reduce() for remove item.
const initialState = {
fetchData: {},
cartData: {}
}
function cartReducer(state = initialState, action) {
switch(action.type) {
case 'ADD_ITEM':
return {...state, cartData: {...state.cartData, ...action.payload}}
case 'EDIT_ITEM':
return {...state, cartData: {...state.cartData, ...action.payload}}
case 'REMOVE_ITEM':
let newState = Object.keys(state.cartData).reduce((r, e) => {
if(!action.payload[e]) r[e] = state.cartData[e];
return r
}, {})
return {...state, cartData: newState}
default:
return state;
}
}
var state = {}
state = cartReducer(undefined, {
type: 'ADD_ITEM',
payload: {"React":{'name': 'React', 'quantity': 1}}
})
console.log(state)
state = cartReducer(state, {
type: 'ADD_ITEM',
payload: {"Node":{'name': 'Node', 'quantity': 2}}
})
console.log(state)
state = cartReducer(state, {
type: 'EDIT_ITEM',
payload: {"React":{'name': 'React', 'quantity': 4}}
})
console.log(state)
state = cartReducer(state, {
type: 'REMOVE_ITEM',
payload: {"React":{'name': 'React', 'quantity': 1}}
})
console.log(state)
It's hard to know exactly what you are trying. Below is an example of a reducer function with an add to cart method. You'll need to add a similar method for each of your scenarios.
export function reducer(state = initialState, action: any): State {
switch(action.type) {
case "ADD_TO_CART": {
return {
fetchData: state.fetchData,
cartData: Object.assign({}, state.cartData, action.payload}
};
}
}
default: {
return state;
}
}
You will then dispatch the action by calling the dispatch function:
dispatch({
type: "ADD_TO_CART",
payload: "React":{'name': 'React', 'quantity': 1}
})
In actions:
const editData = (items) => (dispatch) => {
dispatch({type: 'EDIT_ITEMS', payload: items});
}
In reducer:
const reducer = (state = INITIAL_STATE, action){
case 'EDIT_ITEMS': {
if(_.isEmpty(action.payload)){
return {
...state,
cartData: {},
};
} else {
return {
...state,
cellData: action.payload,
};
}
}
}
This should be the way to do it. payload should be all the items you've in the cart at any point of time.
[EDIT:]
As the question has been edited, You can also do that using deleting a key, using
// Ref: https://github.com/erikras/react-redux-universal-hot-example/issues/962#issuecomment-219354496
export const removeByKey = (object, deleteKey) => {
return Object.keys(object)
.filter(key => key !== deleteKey)
.reduce((result, current) => {
result[current] = object[current];
return result;
}, {});
};
case types.REMOVE_ITEM:
return { ...state, cartData: deleteKey(cartData, action.payload)) }

Categories