React updating data of an array but changes not appears - reactjs

I have a array of groups which is brought from API, and is displayed on a table..
return(
<Table
data={props.data}
columns={columns}
noHeader={true}
/>
)
const mapStateToProps = state => ({
data: state.Table.arrayGroups,
})
In this table I have a column that has a button that enable and disable a group, that calls this action:
export const handleStatus = (data, status, id, endPoint, ActionType) => {
let index = data.findIndex( array => array.id === id);
console.log(index)
if (status) {
console.log('entrei disable')
data[index].enable = false
console.log(data)
return dispatch => {
httpPut(`${endPoint}/${id}/disable`)
.then(resp => {
return [
dispatch({
type: ActionType,
payload: data
})
]
})
.catch(error => {
console.log(error)
})
}
}
else {
console.log('entrei enable')
data[index].enable = true
return dispatch => {
httpPut(`${endPoint}/${id}/enable`)
.then(resp => {
return [
dispatch({
type: ActionType,
payload: data
})
]
})
.catch(error => {
console.log(error)
})
}
}
}
On this action I'm basically getting data array and changing a value inside of it.. and update the arrayGroups.. like this :
case 'MODIFIED_GROUPS':
return { ...state, arrayGroups: action.payload }
the issue is : after doing it table continuous displaying the previous array Data

Connect by react-redux is a PureComponent . What it means is that unless the props change it doesn't trigger a re-render. And it performs a shallow equality and reference check.
Since in your case you are mutating the data, the update isn't triggereing.
Clone the data obejct and update it
export const handleStatus = (data, status, id, endPoint, ActionType) => {
let index = data.findIndex( array => array.id === id);
console.log(index)
const newData = [...data]; // shallow cloning data
if (status) {
console.log('entrei disable')
newData[index].enable = false
console.log(newData);
return dispatch => {
httpPut(`${endPoint}/${id}/disable`)
.then(resp => {
return [
dispatch({
type: ActionType,
payload: newData
})
]
})
.catch(error => {
console.log(error)
})
}
}
else {
console.log('entrei enable')
newData[index].enable = true
return dispatch => {
httpPut(`${endPoint}/${id}/enable`)
.then(resp => {
return [
dispatch({
type: ActionType,
payload: newData
})
]
})
.catch(error => {
console.log(error)
})
}
}
}

Related

Firebase promise - unable to set state, state array is empty

I have a promise to get a firebase collection and then return respective data (all according firebase documentation).
The data is returning fine, the only problem is that it seems that I am unable to set the collection data to a state array. When console.log(chats)this returns an empty array.
The data is returning fine as adding a console.log(doc.data()) in the first then logs the data ok but then it is empty.
What am I doing wrong in the promise?
const HomeScreen: React.FC<Props> = ({ navigation}) => {
const [chats, setChats] = useState<{[key: string]: any}>([]);
useEffect(() => {
const chatsArray = [{}];
db.collection("chats")
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
chatsArray.push({
id: doc.id,
data:doc.data(),
})
});
})
.then(() => {
setChats(chatsArray)
console.log(chats);
})
.catch((error) => {
console.log("Error getting documents: ", error);
});
}, []);
return (
<SafeAreaView>
<ScrollView style={styles.container}>
{chats go here}
</ScrollView>
</SafeAreaView>
)
}
export default HomeScreen;
The console.log(chats); doesn't work, since updating the state is a asynchronous operation that won't have completed by the time you log the value. Try console.log(chatsArray); to see the actual value.
In addition, consider passing the array as the result along the promise chain instead of using a variable in the context:
db.collection("chats")
.get()
.then((querySnapshot) => {
return querySnapshot.docs.map((doc) => {
return {
id: doc.id,
data:doc.data(),
}
});
})
.then((chatsArray) => {
setChats(chatsArray)
console.log(chatsArray);
})
.catch((error) => {
console.log("Error getting documents: ", error);
});
Or simpler and with the same result:
db.collection("chats")
.get()
.then((querySnapshot) => {
const chatsArray = querySnapshot.docs.map((doc) => {
chatsArray.push({
id: doc.id,
data:doc.data(),
})
});
setChats(chatsArray)
console.log(chatsArray);
})
.catch((error) => {
console.log("Error getting documents: ", error);
});

Update Values of Multiple Array in Redux

I'm updating an array and I wanted to update the productCode based on the given newCode response. This is by clicking the 'CREATE ALL PRODUCTS' button.
I'm thinking that the problem is on the reducer. It's currently not updating the productCode and newProductCode
Tip: productIndex is the key to finding it
Click Here: CODESANDBOX
Action
export const createAllProducts = (products) => async (dispatch) => {
try {
dispatch({
type: appConstants.CREATE_ALL_PRODUCTS_REQUEST
});
const responses = [
{
config: null,
data: {
newCode: "NEW_AA"
},
headers: null
},
{
config: null,
data: {
newCode: "NEW_FF"
},
headers: null
},
{
config: null,
data: {
newCode: "NEW_GG"
},
headers: null
}
];
const finalResponses = responses.map((product, index) => ({
newProductCode: product.data.newCode,
productCode: product.data.newCode,
productIndex: products[index].productIndex
}));
console.log(finalResponses);
dispatch({
type: appConstants.CREATE_ALL_PRODUCTS_SUCCESS,
payload: finalResponses
});
} catch (error) {
dispatch({
type: appConstants.CREATE_ALL_PRODUCTS_FAILURE
});
}
};
Reducer
case appConstants.CREATE_ALL_PRODUCTS_SUCCESS:
const updatedProducts = state.products.map((product, index) => {
const found = action.payload.find((el) => el.productIndex === index);
return found
? {
...updatedProducts,
productCode: found.productCode,
newProductCode: found.newProductCode
}
: product;
});
return {
...state,
isCreatingAllProducts: false,
products: updatedProducts
};
The issue is with the reducer
case appConstants.CREATE_ALL_PRODUCTS_SUCCESS:
return {
...state,
products: state.products.map((product, index) => {
const found = action.payload.find((el) => el.productIndex === index);
console.log(found);
return found
? {
...product,
productCode: found.productCode,
newProductCode: found.newProductCode
}
: product;
})
};
You used reduce methods with the initial value state, which is the actually old state.
Consider this example:
const state = { history: null }
const payload = [ 'hello', 'equal' ]
//your current reducer
const newState = payload.reduce((acc, cur) => { acc[cur] = cur; return acc } , state)
//the state reference point to the same obj, then redux will not trigger re-render
console.log(newState === state) // true

Error: Invariant failed: A state mutation was detected between dispatches

Reducer :
function listPeopleByName (state = {
getPeopleName:{}
}, action){
switch(action.type){
case C.LIST_PEOPLE_NAME:{
return {
...state
,getPeopleName :action.payload
}
}
default : {}
}
return state
}
Action:
function listPeopleByName(config) {
return function (dispatch) {
ApiService(config)
.then((resp) => {
dispatch({
type: C.LIST_PEOPLE_NAME,
payload: resp.data,
});
})
.catch((error) => {
dispatch({
type: C.LIST_PEOPLE_NAME,
payload: error,
});
});
};
}
ApiService is a function that make an axios request and returns a respones
Dispatching code :
listPeopleByNameFunction = () => {
const listPeopleByNameParam = {
id: someone,
},
let data = {
PeopleId: "snjenfner",
};
let listPeopleByNameCategory = getApiConfig(
"POST",
listPeopleByNameParam,
data
);
this.props.listPeopleByName(listPeopleByNameCategory);
};
const mapDispatchToProps = (dispatch) => ({
listPeopleByName: (config) => dispatch(listPeopleByName(config)),
});
Although I take the previous state (...state) and change the state with the payload i'm getting, it still shows the state is mutated. I would have used reduxtoolkit but this is a way old project that doesn't need to be migrated to reduxtoolkit.

Redux state updated but component not re-rendered (while using promise)

I am using React/Redux.
The main issue is that when i use Promise then component is not re-rendered, whereas the code is working fine when promise code is not used.
Action Creator
const updateColor = colorobj => {
return dispatch =>
new Promise(function(resolve, reject) {
dispatch(fetchColorBegin());
axios
.post(config.APIURL.color.update, colorobj)
.then(response => {
const data = response.data;
if (data.errorno !== 0) {
dispatch(fetchColorFailure(data.errormsg));
reject(data.errormsg);
} else {
dispatch(updateColorSuccess(colorobj));
resolve('Color Updated');
}
})
.catch(error => {
dispatch(fetchColorFailure(error.message));
reject(error.message);
});
});
};
Reducer
case UPDATE_COLOR_SUCCESS:
const todoIndex = state.data.findIndex(todo => todo.id === action.payload.id);
return update(state, {
loading: { $set: false },
data: { [todoIndex]: { $merge: action.payload } },
error: { $set: null}
});
Component
the state is updated but the component is not updated.
const handleEditOk = values => {
let colorobj = {
id: state.updateData.id,
colorname: values.colorname,
colorcode: values.colorcode,
};
dispatch(updateColor(colorobj))
.then(response => {
message.success(response);
onCancel();
})
.catch(error => {
message.error(error);
});
};
The component update itself only on commenting the promise code.
The problem now is that it is not showing success/failure message.
const handleEditOk = values => {
let colorobj = {
id: state.updateData.id,
colorname: values.colorname,
colorcode: values.colorcode,
};
dispatch(updateColor(colorobj))
// .then(response => {
// message.success(response);
// onCancel();
// })
// .catch(error => {
// message.error(error);
// });
};
Kindly suggest.

Delete item using redux

I am trying to get my head around Redux. Doing something like TODO APP with React and Redux. I can add a new task, update its value, but I cannot delete the item correctly. I get an error Unhandled Rejection (TypeError): Cannot read property 'id' of undefined all the time. I pass the ID to the Delete function just like I do in the Update function. Server side works well. The fetch function itself works, and the delete of item from the database works, but an error occurs on the client side Help please guys
const initialState = {
list: [],
}
export default (state = initialState, action) => {
switch (action.type) {
case GET_TASKS: {
return { ...state, list: action.tasks }
}
case CREATE_TASK: {
return { ...state, list: [...state.list, action.task] }
}
case UPDATE_STATUS: {
return {
...state,
list: state.list.map((it) => {
return action.task.id === it.id ? action.task : it
}),
}
}
case DELETE_TASK: {
return {
list: state.list.map((it) => {
return action.task.id !== it.id ? action.task : it
}),
}
}
default:
return state
}
}
export function getTasks() {
return (dispatch) => {
fetch("/api/task")
.then((r) => r.json())
.then(({ data: tasks }) => {
dispatch({ type: GET_TASKS, tasks })
})
}
}
export function deleteTask(id) {
return (dispatch) => {
fetch(`/api/v1/task/${id}`, {
method: "DELETE",
})
.then((r) => r.json())
.then(({ data: task }) => {
dispatch({ type: DELETE_TASK, task })
})
}
}
My first question would be, in your deleteTask method what is being returned here? Does a delete method actually return the task you deleted?
.then(({ data: task }) => {
dispatch({ type: DELETE_TASK, task })
}
If not, another way you can address this is by changing the task in your dispatch to the id you are passing to the deleteTask method:
dispatch({ type: DELETE_TASK, id });
Then use the filter method instead of map in your reducer to return the tasks that don't match that deleted task's id, effectively "deleting" it from your state:
case DELETE_TASK: {
return {
list: state.list.filter((it) => {
return action.id !== it.id;
}),
}
}

Resources