react component is no updateing the view after deleting an item - reactjs

this the onDelete function which supposed to update the state and the item should disappear from the list once the delete button is pressed
onDelete = async (id) => {
const messages = await Api.deleteMessage(id);
const messageId = id;
const filterdMessages =this.state.messages.filter((message) => {
return message.id !== messageId
});
this.setState({
messages: [...filterdMessages]
});
}
and this what is being rendered
render() {
const {
messages
} = this.state;
const $messages = messages.map((message) => <MessageItem onDelete={this.onDelete} key={message.id} {...message} />);
console.log( $messages);
return (
<section className="messages">
<ul>
{$messages}
</ul>
</section>
)
}
this the messageItem
export default ({ id, body, onResolve, onDelete, license_plate }) => {
const onResolveClick = (event) => {
event.preventDefault();
onResolve(id);
};
const onDeleteClick = (event) => {
event.preventDefault();
onDelete(id);
};
return (
<li className="MessageItem">
<span> <b>{license_plate} </b>{body}</span>
<button onClick={onDeleteClick}>Delete</button>
</li>
)
}

You are using the key as key={message._id} but filtering by message.id.
// v Always undefined, the expression always false.
message.id !== messageId
const filterdMessages = this.state.messages.filter(message => {
// v message.id
return message._id !== messageId;
});

Should be onDelete={() => this.onDelete(message._id)}

There are 2 problems in your code
1) You are not passing the ID in argument
2) You are using _id instead of id for key prop
_onDelete = async (id) => {
await Api.deleteMessage(id);
const { messages } = this.state;
const filterdMessages = messages.filter((message) => message.id !== messageId);
this.setState({ messages: [...filterdMessages]});
}
render() {
const { messages } = this.state;
return (
<section className="messages">
<ul>
{
messages.map(message => (
<MessageItem
onDelete={() => this._onDelete(message.id)}
key={message.id}
{...message}
/>
))
}
</ul>
</section>
)
}

Related

List is disappearing on refresh in react

I'm trying to map out a list to display file information on a grid. I think the issue lies with my keys. When I change the keys, it renders, but on refresh it disappears. I'm using a unique key for each list item.
const FileList = () => {
const { state } = useDB();
return (
<div className='container'>
<div className='fileList-wrapper'>
<div className='fileList-wrapper__header'>Name</div>
<div className='fileList-wrapper__header'>Last modified</div>
<div className='fileList-wrapper__header'>File size</div>
{state.childFolders.map((folder) => {
return <ListItem key={folder.id} folder={folder} />;
})}
</div>
</div>
);
};
Here is the child contents:
const ListItem = ({ folder }) => {
return (
<>
<div
id='fileName'
className='fileList-wrapper__item'
onDoubleClick={handleDoubleClick}>
{folder.name}
</div>
<div className='fileList-wrapper__item'>{folder.timestamp}</div>
<div className='fileList-wrapper__item'>{folder.fileSize}</div>
</>
);
};
Here is the function that sets my state:
const setChildFolders = () => {
const childFolders = [];
database.folders
.where('parentId', '==', state.folderId)
.where('userId', '==', currentUser.uid)
.get()
.then((snapshot) => {
snapshot.forEach((doc) => {
childFolders.push({ id: doc.id, ...doc.data() });
});
})
.catch(() => {});
setState((prev) => ({
...state,
childFolders: childFolders,
}));
};
I'm calling that function in a useEffect:
useEffect(() => {
setChildFolders();
}, [currentFolder, currentUser]);
Just to reiterate, on first render, the list shows, but if I refresh, it disappears. If I then change the keys, it reappears.

How to edit data received from a rest call in react?

I am trying to make a rest call, edit the data and then render it. The problem is that, while editing, I am getting a error - undefined even after checking if the data is there.
component I am making the rest call from:
function Header ({timesheetData, loadTimesheet}) {
useEffect(() => {
loadTimesheet(date)
}, [])
return(
<>
<header className="header">
<div className="wrap">
<span className="btn-icon">
<IconPlus onClick={() => setIsOpen(true)} className="icon icon-plus js-modal-init"/>
</span>
<div className="header-blockquote">
<h1 className="header-quote">{currentQuote.quote}</h1>
<div className="header-cite">{currentQuote.author}</div>
</div>
</div>
<div className="header-inner">
<div className="wrap">
<VegaIcon className="logo" alt="VegaIT"/>
<div className="date-wrap">
<IconCalendar className="icon icon-plus js-modal-init"/>
//
<time>{timesheetData.timesheet.loading ? "asd" : edit(timesheetData) }</time>
//
</div>
</div>
</div>
</header>
</>
)
}
function edit(timesheetData){
let newDate = timesheetData.timesheet.date
newDate = newDate.split("-")
newDate = newDate.reverse()
return newDate.join("/")
}
the redux action:
export const loadTimesheet = (date) => {
let url = "http://localhost:8080/api/timesheet/" + date
return (dispatch) => {
dispatch(getTimesheet)
axios.get(url)
.then((response) => {
const timesheet = response.data
dispatch(getTimesheetSuccess(timesheet))
})
.catch(error => {
const errorMsg = error.message
dispatch(getTimesheetFailure)
})
}
}
Edit: added my mapStateToProps and mapDispatchToProps
const mapStateToProps = state => {
return {
timesheetData: state.timesheet
}
}
const mapDispatchToProps = dispatch => {
return {
loadTimesheet: (date) => dispatch(loadTimesheet(date))
}
}
Edit2: The code: https://codesandbox.io/s/adoring-tharp-o9ibe
use mapStateToProps, mapDispatchToProps:
import getTimesheet from '../actions/...'
Header ({timesheetData, loadTimesheet}) => {
}
const mapDispatchToProps = dispatch => {
return {
loadTimesheet: () => dispatch(getTimesheet()),
}
}
const mapStateToProps = (state) => {
return { timesheetData: state.timesheetData };
};
export default connect(mapStateToProps, mapDispatchToProps)(Header);
also in component:
before render:
check that you have:
timesheetData - not undefined
timesheetData.timesheet - not undefined
timesheetData.timesheet.loading - not undefined
const hasData = timesheetData && !timesheetData.timesheet.loading;
const time = hasData ? edit(timesheetData): "asd";
in render:
<time>{time}</time>

retrieve text from db using draft.js

My draft.js <TextEditor /> populates body with the text e.g: '{"blocks":[{"key":"3mont","text":"lorem ipsum","type":"unstyled","depth":0,"inlineStyleRanges":[],"entityRanges":[],"data":{}}],"entityMap":{}}'.
In Post.js, I want to retrieve and display the formatted text from the db. I've read that in order to do this, I must use convertToRaw() and then convertFromRaw() when retrieving it from the db but I'm having the same problems as this (I'm receiving the cors error and Unexpected token u in JSON at position 0) whenever I use convertFromRaw() and try to retrieve the formatted text from the db.
I have setup my server to support cors so is it because I am trying to parse an invalid response into JSON? Why am I getting the cors error and how can I solve it?
GitHub
How can I get the formatted text from the db in Post.js? Any help would be really appreciated!
CreatePost.js
class CreatePost extends React.Component {
constructor(props) {
super(props);
this.state = {
title: "",
body: EditorState.createEmpty(),
};
}
changeHandler = (e) => {
this.setState({ [e.target.name]: e.target.value });
};
submitHandler = (e) => {
e.preventDefault();
const {
user: { _id },
} = isAuthenticated();
axios({
url: `${API}/post/new-post/${_id}`,
method: "POST",
data: {
...this.state,
body: JSON.stringify(convertToRaw(this.state.body.getCurrentContent())),
},
})
.then((response) => {
// this.setState({ createdPost: this.state.title });
return response
})
.catch((error) => {
if (!this.state.title || !this.state.body) {
this.setState({
error: "This post must contain a title and a body.",
});
}
console.log(error);
});
};
// Attempt to map through blocks
//getText = () => {
// const {body} = this.state;
//const arr = body.blocks.map(({ text }) => text).join(' ')
// console.log(arr)
//}
render() {
const { title, body } = this.state;
return (
<>
<Navbar />
<Tabs>
<TabList>
<Tab>Draft</Tab>
<Tab>Preview</Tab>
</TabList>
<TabPanel>
<div>
<form onSubmit={this.submitHandler}>
<div>
// title input
</div>
<div>
<TextEditor
onChange={(value) => this.setState({ body: value })}
editorState={body}
/>
</div>
<button type="submit">
Publish
</button>
</form>
</div>
</TabPanel>
<TabPanel>
<div>
<h1>{title}</h1>
// display body text value here too
{this.getText()}
</div>
</TabPanel>
</Tabs>
</>
);
}
}
Post.js (display body text)
const [post, setPost] = useState({});
const [error, setError] = useState(false);
const id = props.match.params.id;
const loadSinglePost = (slug, id) => {
read(slug, id).then((data) => {
if (error) {
console.log(data.error);
setError(data.error);
} else {
setPost(data)
console.log(data);
}
});
};
useEffect(() => {
const slug = props.match.params.slug;
loadSinglePost(slug, id);
}, [props]);
return (
<>
<div>
<h3>{post.title}</h3>
...
// display text value below
<p>{post.body}</p>
</div>
</div>
</>
);
};
TextEditor.js
class TextEditor extends React.Component {
constructor(props) {
super(props);
this.plugins = [addLinkPlugin];
}
toggleBlockType = (blockType) => {
this.props.onChange(RichUtils.toggleBlockType(this.props.editorState, blockType));
};
handleKeyCommand = (command) => {
const newState = RichUtils.handleKeyCommand(
this.props.editorState,
command
);
if (newState) {
this.props.onChange(newState);
return "handled";
}
return "not-handled";
};
onUnderlineClick = () => {
this.props.onChange(
RichUtils.toggleInlineStyle(this.props.editorState, "UNDERLINE")
);
};
onBoldClick = (event) => {
this.props.onChange(RichUtils.toggleInlineStyle(this.props.editorState, "BOLD"));
};
onItalicClick = () => {
this.props.onChange(
RichUtils.toggleInlineStyle(this.props.editorState, "ITALIC")
);
};
onAddLink = () => {
const editorState = this.props.editorState;
const selection = editorState.getSelection();
const link = window.prompt("Paste the link -");
if (!link) {
this.props.onChange(RichUtils.toggleLink(editorState, selection, null));
return "handled";
}
const content = editorState.getCurrentContent();
const contentWithEntity = content.createEntity("LINK", "MUTABLE", {
url: link,
});
const newEditorState = EditorState.push(
editorState,
contentWithEntity,
"create-entity"
);
const entityKey = contentWithEntity.getLastCreatedEntityKey();
this.props.onChange(RichUtils.toggleLink(newEditorState, selection, entityKey));
};
toggleBlockType = (blockType) => {
this.props.onChange(RichUtils.toggleBlockType(this.props.editorState, blockType));
};
render() {
return (
<div>
// formatting buttons
<div>
<Editor
blockStyleFn={getBlockStyle}
editorState={this.props.editorState}
handleKeyCommand={this.handleKeyCommand}
onChange={this.props.onChange}
plugins={this.plugins}
placeholder="Post Content"
/>
</div>
</div>
);
}
}

Populating stateless child component props from parent component state to display nested unordered list in React (with Firebase)

I'm probably missing something very simple here, but I can't get my code to run properly. I'm trying to display a nested unordered list of referenceListItems for every referenceList. The main question I guess is how do I pass the state variable referenceListItems into the child component ReferenceListItems?
const ReferencePage = () => (
<div>
<h1>Reference</h1>
<Reference />
</div>
);
class ReferenceBase extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
referenceLists: [],
referenceListItems: [],
};
}
componentDidMount() {
this.onListenForReferenceLists();
this.onListenForReferenceListItems();
}
onListenForReferenceLists() {
this.setState({ loading: true });
this.unsubscribeReferenceLists = this.props.firebase
.referenceLists()
.onSnapshot(snapshot => {
if (snapshot.size) {
let referenceLists = [];
snapshot.forEach(doc =>
referenceLists.push({ ...doc.data(), uid: doc.id }),
);
this.setState({
referenceLists: referenceLists,
loading: false
});
} else {
this.setState({ referenceLists: null, loading: false });
}
});
}
onListenForReferenceListItems() {
this.unsubscribeReferenceListsItems = this.props.firebase
.referenceListItems()
.onSnapshot(snapshot => {
if (snapshot.size) {
let referenceListItems = [];
snapshot.forEach(doc =>
referenceListItems.push({ ...doc.data(), uid: doc.id }),
);
this.setState({
referenceListItems: referenceListItems,
loading: false
});
} else {
this.setState({ referenceListItems: null, loading: false });
}
});
}
componentWillUnmount() {
this.unsubscribeReferenceLists();
this.unsubscribeReferenceListsItems();
}
render() {
const { referenceLists, referenceListItems, loading } = this.state;
return (
<div>
{loading && <div>Loading ...</div>}
{referenceLists ? (
<ReferenceLists referenceLists={referenceLists} />
):(
<div>There are no reference items ...</div>
)}
</div>
);
}
}
const Reference = withFirebase(ReferenceBase);
const ReferenceLists = ({ referenceLists }) => (
<ul className="reference-lists">
{referenceLists.map( referenceList => (
<ReferenceList key={referenceList.uid} referenceList={referenceList} />
))}
</ul>
);
const ReferenceList = ({ referenceList }) => (
<li className="reference">
<strong>{referenceList.userId}</strong> {referenceList.name}
<ReferenceListItems />
</li>
);
const ReferenceListItems =({ referenceListItems }) => (
<ul className="reference-list-items">
{referenceListItems.map( referenceListItem => (
<ReferenceListItem key={referenceListItem.uid} referenceListItem={referenceListItem} />
))}
</ul>
);
const ReferenceListItem = ({ referenceListItem }) => (
<li className="reference-list-item">
<strong>{referenceListItem.userId}</strong> {referenceListItem.name}
</li>
);
You do not explicitly use your ReferenceListItems inside the parent ReferenceBase component. So you'll just have to pass it down as a property throughout the component tree.
render() {
const { referenceLists, referenceListItems, loading } = this.state;
return (
<div>
{loading && <div>Loading ...</div>}
{referenceLists ? (
<ReferenceLists referenceLists={referenceLists} referenceListItems={referenceListItems} />
):(
<div>There are no reference items ...</div>
)}
</div>
);
ReferenceLists
const ReferenceLists = ({ referenceLists, referenceListItems }) => (
<ul className="reference-lists">
{referenceLists.map( referenceList => (
<ReferenceList key={referenceList.uid} referenceList={referenceList} referenceListItems={referenceListItems} />
))}
</ul>
ReferenceList
const ReferenceList = ({ referenceList, referenceListItems }) => (
<li className="reference">
<strong>{referenceList.userId}</strong> {referenceList.name}
<ReferenceListItems referenceListItems={referenceListItems}/>
</li>
);
By redeclaring referenceList
let referenceLists = [];
it never gets set in your class. you either need to return the referenceLists inside the closure or set the class level variable in your callback
this.referenceLists.push({ ...doc.data(), uid: doc.id })

React, Redux Deleting an item on click deletes all items

I need fresh eyes on this. As I am slowly learning React and Redux i have run into a roadblock again.
/actions/items.js
export const DELETE_ITEM = "DELETE_ITEM"
export function deleteItem(id) {
return {
type: DELETE_ITEM,
id
}
}
/components/Item.jsx
export default class Item extends React.Component {
renderDelete = () => {
return <button onClick={this.props.onDelete}>x</button>
};
renderItem = () => {
const onDelete = this.props.onDelete
return (
<div onClick={this.edit}>
<span>{this.props.text}</span> {onDelete ? this.renderDelete() : null}
</div>
)
}
/components/Items.jsx
export default class Items extends React.Component {
handleOnDelete = (id) => {
this.props.dispatch(actions.deleteItem(id))
}
render() {
const {items, onEdit, onDelete } = this.props
return (
<ul>{items.map(item =>
<li key={item.id}>
<Item
id={item.id}
text={item.text}
onEdit={this.handleOnEdit}
onDelete={this.handleOnDelete.bind(null, item.id)}
/>
</li>
)}</ul>
);
}
}
export default connect(
state => ({
items: state.items
})
)(Items)
/reducers/items.js
case types.DELETE_ITEM:
const filteredItems = state.filter((item) => {
item.id !== action.id
});
return filteredItems
I'm not sure why clicking on x button to delete an item deletes all of them. Thanks in advance for the help
You do not return value in filter in your reducers.
Your should add return:
const filteredItems = state.filter((item) => {
return item.id !== action.id;
});
Or use short version, without brackets:
const filteredItems = state.filter((item) => item.id !== action.id);

Resources