Im trying to delete messages written in a form with react and redux.
The id props is sent correctly as I can see in console, but I only get my error msg when I press the delete button.
This is the button component:
import React from 'react'
import { useDispatch } from 'react-redux'
import { messages, fetchDeleteMessage } from 'reducer/messages'
export const DeleteBtn = (props) => {
const dispatch = useDispatch()
const handleDeleteMessageClick = () => {
dispatch(fetchDeleteMessage(props.message.id))
console.log('delete message', (props.message.id))
}
return (
<button className="delete-btn"
onClick={handleDeleteMessageClick}>
<span role="img" aria-label="delete">✖︎</span>
</button>
)
}
This is my reducer where I try to fetch and delete a specific message upon its id, the id is passed on to the fetch correctly, but nothing happens and I cant see whats wrong and feel I tried it all (....apparently not)
import { createSlice } from '#reduxjs/toolkit'
export const messages = createSlice({
name: 'messages',
initialState: {
allMessages: [],
},
reducers: {
deleteMessage: (state, action) => {
console.log('deleteMessageState', state)
console.log('deleteMessageAction', action)
//finds the task
//remove it from the array
state.allMessages = state.allMessages.filter((message) => message.id !== action.payload)
},
}
})
//****** fetch DELETE message ********
export const fetchDeleteMessage = (id) => {
return (dispatch) => {
fetch(`http://localhost:3004/messages/${id}`, {
method: 'DELETE',
statusCode: 204,
headers: {
'Content-Type': 'application/json'
}
})
.then((res) => res.json())
.then(json => {
console.log('DELETE', json, id)
dispatch(messages.action.deleteMessage(id))
})
.catch(err => {
console.error('error', err)
dispatch(messages.actions.deleteMessage({ error: `Error, failed to delete` }))
})
}
}
`````
To delete an item you should return state
return state.allMessages.filter((message) => message.id !== action.payload)
instead of
state.allMessages = state.allMessages.filter((message) => message.id !== action.payload)
Related
I am using react redux to create a basic blogs CRUD app.
I have a blog list which have a read more button which when click should take me to that particular blog. I am trying to get into that particular blog using blog id and using useParam Hook. You can find my code below -
acion-creators - Actions
export const listBlog = (blogList) => {
return (dispatch) => {
dispatch({
type: "list-blog",
payload: blogList
})
}
}
export const addBlog = (blog) => {
return (dispatch) => {
dispatch({
type: "add-blog",
payload: blog
})
}
}
export const deleteBlog = (id) => {
return (dispatch) => {
dispatch({
type: "delete-blog",
payload: id
})
}
}
export const findBlog = (id) => {
return (dispatch) => {
dispatch({
type: "find-blog",
payload: id
})
}
}
reducer - blogReducer -
import blogs from "../data"
const reducer = (state=blogs, action) => {
if (action.type === "list-blog") {
return state
}
else if (action.type === "add-blog"){
state.push(action.payload)
return state
}
else if (action.type === "delete-blog") {
state.pop(action.payload)
return state
}
else if (action.type === "find-blog") {
for(let i=0; i<=state.length; i++){
if(state[i].id === action.payload){
console.log(state[i])
return state[i]
}
else{
return "Blog not Found"
}
}
}
else {
return state
}
}
export default reducer
Blog page which should show that selected blog after clicking read more - BlogPage.js
import React, { useEffect, useState } from "react";
import { useDispatch } from "react-redux";
import { useParams } from "react-router-dom";
import { bindActionCreators } from "redux";
import { actionCreators } from "../State/index";
export const BlogPage = () => {
const [blogId, setBlogId] = useState();
const dispatch = useDispatch();
const actions = bindActionCreators(actionCreators, dispatch)
const params = useParams()
useEffect(() => {
setBlogId(params.blogId)
},[])
const handleEdit = (id) => {
console.log(id)
const blog = actions.findBlog(id)
console.log(blog)
}
return (
<div className="container">
<h3></h3>
<p>
Body
</p>
<button className="btn btn-success my-3" onClick={() => handleEdit(blogId)}>Edit Blog</button>
<button className="btn btn-danger mx-3">Delete Blog</button>
<hr className="my-3"/>
<button className="btn btn-primary">Like</button>
<br /> <br />
<h4 className="mx-3">Comments</h4>
</div>
)
}
In BlogPage.js I am finding the selected blog Id using useParam and using that in find-blog Action to find that particular blog in my state.
State is a list of blogs stored locally.
I am getting the id when I console.log the id of the blog but I am getting undefined when I try to use actions.findBlog()
It is showing undefined when I console.log the blog I find using the findBlog action.
I'm trying to display the response from the API into my react component but it's not working. If I try to use it in the console, I can see the data and its value but not in the react component, it's empty when I try to show the value in a div.
Here is the code where I'm trying to display it in my react component:
const CharacterListing = () => {
const characters = useSelector(getAllCharacters);
console.log("Hello", characters);
const renderCharacters = Object.entries(characters).map(([key, value]) => {
console.log(value.name);
<div>{value.name}</div>
})
return (
<div>
{renderCharacters}
</div>
);
};
export default CharacterListing;
This is the code for my Character Slice Component
const initialState = {
characters: {},
};
const characterSlice = createSlice({
name: 'characters',
initialState,
reducers: {
addCharacters: (state, { payload }) => {
state.characters = payload;
},
},
});
export const { addCharacters } = characterSlice.actions;
export const getAllCharacters = (state) => state.characters.characters;
export default characterSlice.reducer;
This is the code for my Home Component:
const Home = () => {
const dispatch = useDispatch();
useEffect(() => {
const fetchCharacters = async () => {
const response = await baseURL.get(`/characters`)
.catch(error => {
console.log("Error", error);
});
dispatch(addCharacters(response.data));
console.log("Success", response);
};
fetchCharacters();
}, [])
return (
<div>
Home
<CharacterListing />
</div>
);
};
export default Home;
Thank you
You forgot to return item into your map func
Try this :
const renderCharacters = Object.entries(characters).map(([key, value]) => {
console.log(value.name);
return <div key={key}>{value.name}</div>
})
I have a react component that has a html button that when clicked calls a function that adds an element to a redux reducer and then redirects to another component. The component that is redirected to needs to set state from the reducer but it won't. I know that it is being added to the array in the reducer because I wrote it as an async await and it redirects after it gets added.
This is the original component
const Posts = () => {
const dispatch = useDispatch();
const getProfile = async (member) => {
await dispatch({ type: 'ADD_MEMBER', response: member })
console.log(member)
window.location.href='/member'
console.log('----------- member------------')
console.log(post)
}
const socialNetworkContract = useSelector((state) => state.socialNetworkContract)
return (
<div>
{socialNetworkContract.posts.map((p, index) => {
return <tr key={index}>
<button onClick={() => getProfile(p.publisher)}>Profile</button>
</tr>})}
</div>
)
}
export default Posts;
This is the 'socialNetworkContract' reducer
import { connect, useDispatch, useSelector } from "react-redux";
let init = {
posts:[],
post:{},
profiles:[],
profile:{},
members:[],
member:{}
}
export const socialNetworkContract = (state = init, action) => {
const { type, response } = action;
switch (type) {
case 'ADD_POST':
return {
...state,
posts: [...state.posts, response]
}
case 'SET_POST':
return {
...state,
post: response
}
case 'ADD_PROFILE':
return {
...state,
profiles: [...state.profiles, response]
}
case 'SET_PROFILE':
return {
...state,
profile: response
}
case 'ADD_MEMBER':
return {
...state,
members: [...state.members, response]
}
case 'SET_MEMBER':
return {
...state,
member: response
}
default: return state
}
};
and this is the component that the html button redirects to
const Member = () => {
const [user, setUser] = useState({})
const [profile, setProfile] = useState({});
const dispatch = useDispatch();
useEffect(async()=>{
try {
const pro = socialNetworkContract.members[0];
setUser(pro)
const p = await incidentsInstance.usersProfile(user, { from: accounts[0] });
const a = await snInstance.getUsersPosts(user, { from: accounts[0] });
console.log(a)
setProfile(p)
} catch (e) {
console.error(e)
}
}, [])
const socialNetworkContract = useSelector((state) => state.socialNetworkContract)
return (
<div class="container">
{socialNetworkContract.posts.map((p, index) => {
return <tr key={index}>
{p.message}
{p.replies}
</tr>})}
</div>
</div>
</div>
</div>
)
}
export default Member;
This is the error I get in the console
Error: invalid address (arg="user", coderType="address", value={})
The functions I'm calling are solidity smart contracts and the have been tested and are working and the element I'm trying to retrieve out of the array is an ethereum address.
incidentsInstance and snInstance are declared in the try statement but I took a lot of the code out to make it easier to understand.
given setUser is async, your user is still an empty object when you make your request.
you could pass pro value instead:
useEffect(async () => {
try {
const pro = socialNetworkContract.members[0];
setUser(pro)
const p = await incidentsInstance.usersProfile(pro, { from: accounts[0] });
const a = await snInstance.getUsersPosts(pro, { from: accounts[0] });
setProfile(p)
} catch (e) {
console.error(e)
}
}, [])
or break your useEffect in two pieces:
useEffect(() => {
setUser(socialNetworkContract.members[0]);
}, [])
useEffect(async () => {
if (!Object.keys(user).length) return;
try {
const p = await incidentsInstance.usersProfile(user, { from: accounts[0] });
const a = await snInstance.getUsersPosts(user, { from: accounts[0] });
console.log(a)
setProfile(p)
} catch (e) {
console.error(e)
}
}, [user])
note: fwiw, at first sight your user state looks redundant since it's derived from a calculated value.
I'm working on a todo app and everything works fine when it first loads in. However, when I add a new todo and the store updates, I get the unique key warning, when the key is defined in the array components:
render() {
const todoList = this.props.todos.map(todo => {
return <Todo todo={todo} key={todo._id}/>
})
return (
<div className={styles.todoContainer}>
{todoList}
</div>
);
}
Todo Component:
return (
<div className={styles.todo}>
<h2 className={styles.todoText}>{props.todo.name}</h2>
</div>
);
Adding todo:
//actions.js
export function addTodo(todo){
let config = {
headers: {
token: localStorage.getItem('token')
}
};
return function(dispatch){
return axios.post('http://localhost:8082/api/todos/create', todo, config)
.then(msg => {
dispatch({type: ADD_TODO, payload: todo})
})
.catch(err => console.log(err));
}
}
//reducer.js
case ADD_TODO:
const data = [...state.data];
data.push(action.payload);
return {
...state,
data: data
};
Is this a problem I should worry about fixing, or is it a bug? Thanks!
I want to implement an action which gets item by id, so I've created fetchItemAction(), as follows:
export const fetchItemAction = () => (dispatch) => {
dispatch({
type: FETCH_ITEM_REQUEST,
});
return axios.get(`${url}/notes/5d4724cd62087b0e141f75a4`)
.then(({ data }) => {
console.log(data);
dispatch({
type: FETCH_ITEM_SUCCESS,
data,
});
})
.catch((error) => {
dispatch({
type: FETCH_ITEM_FAILURE,
});
});
};
Then, I try to set item field in State in my reducer:
const initialState = {
isAuthenticated: false,
user: {},
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_ITEM_REQUEST:
return {
...state,
isLoading: true,
};
case FETCH_ITEM_SUCCESS:
return {
...state,
item: action.data,
isLoading: false,
};
}
};
Then, I try to get those data in Details component:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchItemAction } from 'actions/actions';
class Details extends Component {
componentDidMount() {
const { fetchItem } = this.props;
fetchItem();
}
render() {
const { item, isLoading } = this.props;
return (
<>
{console.log(item)}
{/* <p>{item.title}</p> */}
</>
);
}
}
const mapStateToProps = ({ item, isLoading }) => ({ item, isLoading });
const mapDispatchToProps = dispatch => ({
fetchItem: () => dispatch(fetchItemAction()),
});
export default connect(mapStateToProps, mapDispatchToProps)(Details);
As a result, I'm getting following data in console:
Apart from two undefinded the result looks good because there is correct response from my backend.
But, when I try to uncomment <p>item.title</p> line in Details.js, the app crash:
TypeError: Cannot read property 'title' of undefined
I also implemented correctly fetchItemsAction(), addItemAction() and deleteItemAction() which are very similar but I have no idea what is wrong in fetchItemAction().
This is an asynchronous issue. componentDidMount is called when the component is mounted. Then, you're calling fetch. So on your first render, item is undefined. Once the data is returned, the render is triggered again with item data.
So, just check if item is defined:
render() {
const { item, isLoading } = this.props;
return (
<>
{console.log(item)}
{item && <p>{item.title}</p>}
</>
);
}