I want to delete it.But it won't be deleted.
If you click the delete text in the modal, it should be deleted, but it doesn't work.What should I do to delete it?
There's an error saying that onRemove is not a function.Please help me.
I want to delete it.But it won't be deleted.
If you click the delete text in the modal, it should be deleted, but it doesn't work.What should I do to delete it?
There's an error saying that onRemove is not a function.Please help me.
export default function Modal({ onRemove, id }) {
return (
<OptionModalWrap>
<ModalWrapper>
<TitleWrap>Edit</TitleWrap>
<TitleWrap>Duplicate</TitleWrap>
<DeleteText onClick={() => onRemove(id)}>Delete</DeleteText>
</ModalWrapper>
</OptionModalWrap>
);
}
export default function GroupList({ title, onRemove }) {
const [showModal, setShowModal] = useState(false);
const optionModal = () => {
setShowModal(prev => !prev);
};
return (
<AdGroups>
<Header>
<Container>
<ActiveWrap>
<ActiveIcon src={Active} />
<SubTitle>Active</SubTitle>
</ActiveWrap>
<Alaram>
<Bell src={bell} />
<Text className="alarmCount">10</Text>
</Alaram>
</Container>
<EtcIconWrap>
<EtcIcon src={plus} onClick={optionModal} />
{showModal && (
<OptionModal showModal={showModal} onRemove={onRemove} />
)}
</EtcIconWrap>
</Header>
<GroupTitle>{title}</GroupTitle>
</AdGroups>
);
}
export default function GroupPage() {
const [Groupdata, setGroupData] = useState([]);
const onRemove = item => {
setGroupData(Groupdata.filter(item => item.id !== item));
};
useEffect(() => {
fetch('./data/AdsGroup/AdsGroupList.json')
.then(res => res.json())
.then(res => setGroupData(res));
}, []);
return (
<GroupPages>
{Groupdata.map(item => {
return (
<GroupList key={item.id} title={item.title} onRemove={onRemove} />
);
})}
</GroupPages>
);
}
You have not passed the id in GroupList and then also to the OptionModal component.
So here is the revised code:
Group Page Component:
Passing the id to GroupList Component
const onRemove = id => {
setGroupData(Groupdata.filter(item => item.id !== id)); // you were item.id !== item which was wrong
};
<GroupList key={item.id} title={item.title} id={item.id} onRemove={onRemove} /> // passing the id
Group List Component:
Added id in the props and passed that to Modal Component. Also calling optionModal function to close the Modal after it deleted
export default function GroupList({ id, title, onRemove }) {
const [showModal, setShowModal] = useState(false);
const optionModal = () => {
setShowModal(prev => !prev);
};
return (
<AdGroups>
<Header>
<Container>
<ActiveWrap>
<ActiveIcon src={Active} />
<SubTitle>Active</SubTitle>
</ActiveWrap>
<Alaram>
<Bell src={bell} />
<Text className="alarmCount">10</Text>
</Alaram>
</Container>
<EtcIconWrap>
<EtcIcon src={plus} onClick={optionModal} />
{showModal && (
<OptionModal id={id} showModal={showModal} onRemove={onRemove;optionModal} />
)}
</EtcIconWrap>
</Header>
<GroupTitle>{title}</GroupTitle>
</AdGroups>
);
}
Modal Component: No change in this component
export default function Modal({ onRemove, id }) {
return (
<OptionModalWrap>
<ModalWrapper>
<TitleWrap>Edit</TitleWrap>
<TitleWrap>Duplicate</TitleWrap>
<DeleteText onClick={() => onRemove(id)}>Delete</DeleteText>
</ModalWrapper>
</OptionModalWrap>
);
}
Didn't your IDE complaint about this piece of code? both of the onRemove & filter functions' props are called item, it shouldn't be.
const onRemove = itemId => {
setGroupData(Groupdata.filter(item => item.id !== itemId));
};
Related
As said in title, my props is an empty object.
This is my component, in which i want to match a proper object in mapStateToProps.
The matching object exists, because when i pass and x.id === 1 , the object is being rendered.
const UserDetails = ({ episode, history }, props) => {
const { id } = useParams();
// const handleDelete = (id) => {
// if (window.confirm("Are you sure wanted to delete the episode ?")) {
// dispatch(deleteEpisode(id));
// }
// };
console.log("hej", props); // it prints empty object
console.log(episode);
return (
<div>
{episode ? (
<div>
<button onClick={() => history.push("/episodes")}>...back</button>
<h1> Tytuł: {episode.title}</h1>
<h3> Data wydania: {episode.release_date}</h3>
<h3> Series: {episode.series} </h3>
<img src={episode.img} alt="episode" />
{/* <div>
{episode.episode.characters.map((id) => {
const properActor = users.find((el) => el.id == id);
return <div>{`${properActor.name} `}</div>;
})}
</div> */}
<button onClick={() => history.push(`/episode/edit/${episode.id}`)}>
Edit
</button>
{/* <button onClick={() => handleDelete(episode.id)}>Delete</button> */}
<div>
<Link to={`/episodes/${episode.id}/addCharacter`}>
<button type="button">Dodaj postać do: {episode.title}</button>
</Link>
</div>
</div>
) : (
<div>Loading...</div>
)}
</div>
);
};
const mapStateToProps = (state, props) => {
return {
episode: state.episodes.episodes
? state.episodes.episodes.find((x) => x.id === props.match.params.id)
: null,
};
};
export default withRouter(connect(mapStateToProps, null)(UserDetails));
for anyone, who woudl like to see the whole project:
https://stackblitz.com/edit/node-fxxjko?file=db.json
hope it works,
to run the database u have to install npm json-server and run
EDIT:
If i do something like this:
const mapStateToProps = (state, props) => {
console.group("mapStateToProps");
console.log(props); // Does props.match.params.id exist? What is it?
console.log(state.episodes.episodes); // Is there an object in this array whose id matches the above?
console.groupEnd();
return {
episode: state.episodes.episodes
? state.episodes.episodes.find(
(x) => x.episodeId === props.match.params.episodeId
)
: null,
};
};
i see the this result:
https://imgur.com/a/ssrJjHV
You are not properly destructuring the rest of your props. Implement ellipsis to get the rest of the props back
It should be:
const UserDetails = ({ episode, history, ...props}) => {
//rest of your code
}
not:
const UserDetails = ({ episode, history }, props) => {
//rest of your code
}
My Onclick on bestmovies map does not work. If I place it on a H1, for example, works. onClick={handleClickMovie}
// imports....
const Movies = () => {
const [popularMovies, setPopularMovies] = useState([])
const [bestMovies, setBestMovies] = useState([])
const [showPopUp, setShowPopUp] = useState(false)
const handleClickMovie = () => {
setShowPopUp(console.log('Clicked'))
}
useEffect(() => {
async function getMovies() {
const responsePopularMovies = await getPopularMovies()
setPopularMovies(responsePopularMovies.results)
const responseBestMovies = await getBestMovies()
setBestMovies(responseBestMovies.results)
}
getMovies()
}, [])
return (
<div>
<Wrapper>
{showPopUp ? <MoviePopUp /> : null}
<h1>Filmes Populares</h1>
<Content>
{popularMovies.map(item => (
<MovieItem movie={item} />
))}
</Content>
<h1>Filmes Bem Avaliados</h1>
<Content>
{bestMovies.map(item => (
<MovieItem movie={item} onClick={handleClickMovie} />
))}
</Content>
</Wrapper>
</div>
)
}
export default Movies
MovieItem.js
import React from 'react'
import { Cover, Score, Title } from './MovieItem.styles'
const MovieItems = ({ movie }) => {
return (
<Cover key={movie.id}>
<img
src={`https://image.tmdb.org/t/p/original${movie.poster_path}`}
alt="capas"
/>
<Score>{movie.vote_average}</Score>
<Title>{movie.title}</Title>
</Cover>
)
}
export default MovieItems
try wrapping in a div
<Content>
{bestMovies.map(item => (
<div onClick={handleClickMovie}>
<MovieItem movie={item} onClick={handleClickMovie} />
</div>
))}
</Content>
As #anthony_718 answered, you are calling onClick on a JSX component. JSX components aren't in the DOM and don't have click events (although they can render HTML elements if they contain them).
If you want, you can also pass the props all the way up to an actual html element the <Cover> renders.
#anthony_718's answer is correct.
The reason it didn't work it's because <MovieItem> doesn't have onClick in his props.
However, to facilitate reusability, you can modify your component like so:
const MovieItems = ({ movie, onClick }) => {
return (
<div onClick={onClick}>
<Cover key={movie.id}>
// ... rest of your stuff
</Cover>
</div>
)
}
export default MovieItems
It's essentially the same solution, but by placing <div onClick> within the component definition, you make it more reusable than the other option.
check this
bestMovies.map((item, i) => { return( <MovieItem movie={item} onClick={handleClickMovie} /> )})
I'm trying to figure out how to edit a todo item in my react app using hooks, but I can't seem to figure out how to write the code.
Most of the solutions I've seen online are using class components and it's not written with the same logic as my app.
Here is my current code
function TodoList() {
const [todos, setTodos] = useState([]);
const addTodo = todo => {
if (!todo.text || /^\s*$/.test(todo.text)) {
return;
}
const newTodos = [todo, ...todos];
setTodos(newTodos);
console.log(newTodos);
};
const removeTodo = id => {
const removedArr = [...todos].filter(todoId => todoId.id !== id);
setTodos(removedArr);
};
const completeTodo = id => {
let updatedTodos = todos.map(todo => {
if (todo.id === id) {
todo.isComplete = !todo.isComplete;
}
return todo;
});
setTodos(updatedTodos);
};
const editTodo = e => {
setTodos(e.target.value);
};
return (
<>
<TodoForm onSubmit={addTodo} />
{todos.map(todo => (
<div>
<div
key={todo.id}
className={todo.isComplete ? 'complete' : ''}
key={todo.id}
onClick={() => completeTodo(todo.id)}
>
{todo.text}
</div>
<FaWindowClose onClick={() => removeTodo(todo.id)} />
</div>
))}
</>
);
}
Here is the code from the other component
function TodoForm(props) {
const [input, setInput] = useState('');
const handleChange = e => {
setInput(e.target.value);
};
const handleSubmit = e => {
e.preventDefault();
props.onSubmit({
id: Math.floor(Math.random() * 10000),
text: input,
complete: false
});
setInput('');
};
return (
<form onSubmit={handleSubmit}>
<input
placeholder='todo...'
value={input}
onChange={handleChange}
name='text'
/>
<button onClick={handleSubmit}>add todo</button>
</form>
);
}
So right now everything works where I can add todos and delete todos + cross out todos. Only thing missing is being able to edit them.
I saw some suggestions about updating the text value with an input form, but I'm not too sure how I'd implement that in my editTodo function.
Similar to your removeTodo handler, you want to pass the todo.id to completeTodo.
<div className={todo.isComplete ? "complete" : ""} key={todo.id} onClick={() => completeTodo(todo.id)}>
Then you would update a bool value in the todo object.
const completeTodo = (id) => {
let updatedTodos = todos.map(todo => {
if(todo.id === id){
todo.isComplete = true
}
return todo
})
setTodos(updatedTodos)
};
Edit: add styling strikethrough
You'll then conditionally add a css style based on isComplete boolean
CSS
.complete {
text-decoration: line-through;
}
To be able to click on the Remove button, place it outside the todo div in your map function.
{todos.map((todo, isComplete) => (
<>
<div
key={todo.id}
onClick={completeTodo}
className={isComplete ? 'complete' : ''}
>
{todo.text}
</div>
<FaWindowClose onClick={() => removeTodo(todo.id)} />
</>
))}
As discussion with you in another question here it is:
TodoList.js
import React, { useState } from "react";
import TodoForm from "./TodoForm";
import Todo from "./Todo";
function TodoList({ onClick }) {
const [todos, setTodos] = useState([]);
//Track is edit clicked or not
const [editId, setEdit] = useState(false);
//Save input value in input box
const [inputValue, setInputValue] = useState("");
const handleEditChange = (id, text) => {
setEdit(id);
setInputValue(text);
};
const addTodo = (todo) => {
if (!todo.text || /^\s*$/.test(todo.text)) {
return;
}
const newTodos = [todo, ...todos];
setTodos(newTodos);
console.log(newTodos);
};
const removeTodo = (id) => {
const removedArr = [...todos].filter((todoId) => todoId.id !== id);
setTodos(removedArr);
};
const completeTodo = (id) => {
let updatedTodos = todos.map((todo) => {
if (todo.id === id) {
todo.isComplete = !todo.isComplete;
}
return todo;
});
setTodos(updatedTodos);
};
const editTodo = (id, text) => {
let editTodos = todos.map((todo) => {
if (todo.id === id) {
todo.text = text;
}
return todo;
});
setTodos(editTodos);
setEdit(false);
};
return (
<>
<TodoForm onSubmit={addTodo} />
{/* I want to move this code below into a new component called Todo.js */}
<Todo
todos={todos}
completeTodo={completeTodo}
removeTodo={removeTodo}
editTodo={editTodo}
handleEditChange={handleEditChange}
editId={editId}
inputValue={inputValue}
setInputValue={setInputValue}
/>
</>
);
}
export default TodoList;
Todo.js
// I want to move this code into this component
import React, { useState } from "react";
import { FaWindowClose, FaRegEdit } from "react-icons/fa";
const Todo = ({
todos,
completeTodo,
removeTodo,
editTodo,
editId,
handleEditChange,
inputValue,
setInputValue
}) => {
return todos.map((todo) => (
<div className="todo-row">
{editId === todo.id ? (
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
) : (
<div
key={todo.id}
className={todo.isComplete ? "complete" : ""}
onClick={() => completeTodo(todo.id)}
>
{todo.text}
</div>
)}
{editId === todo.id ? (
<button onClick={() => editTodo(todo.id, inputValue)}>Edit todo</button>
) : (
<>
<FaWindowClose onClick={() => removeTodo(todo.id)} />
<FaRegEdit onClick={() => handleEditChange(todo.id, todo.text)} />
</>
)}
</div>
));
};
export default Todo;
Make sure you read and understand code first. Logic is pretty simple what you do in completeTodo. You just need to update text part. Tricky part is to open in input. So logic is like track if user click on id set that id. And check if id is there open input with that id value other wise normal one.
Here is demo of this POC: https://codesandbox.io/s/nostalgic-silence-idm21?file=/src/Todo.js:0-1059
import React, { useState, useEffect, useCallback } from "react";
export const Root = () => {
const [items, setItems] = useState(["A", "B"]);
const _onClick = useCallback( item => {
return () =>alert(item);
},[]);
return (
<>
<button onClick={() => setItems(["A", "B", "C"])}>Button</button>
{items.map((item, index) => (
<Item key={index} item={item} onClick={_onClick(item)} />
))}
</>
);
};
const Item = React.memo(({ item, onClick }) => {
useEffect(() => {
console.log("Item: ", item);
});
return <button onClick={onClick}>{item}</button>;
});
How do we stop the re-rendering of A and B?
The result I want is to be a memo on the console when the button is pressed and "Item: C".
Because onClick of <Item/> is new every time it is rendered, it will cause A and B to re-render.
You can use React.memo second parameter to check, for example:
const Item = React.memo(({ item, onClick }) => {
// ...
return <button onClick={onClick}>{item}</button>;
}, (prevProps, nextProps) => {
console.log(Object.is(prevProps.onClick, nextProps.onClick)); // console: false
});
More see doc.
In your code, _onClick(item) will return new callback every render.
<Item key={index} item={item} onClick={_onClick(item)} />
You can change _onClick to this:
const _onClick = useCallback(item => alert(item), []);
Next, pass _onClick to Item, and change how button's onClick is executed.
<Item key={index} item={item} onClick={_onClick} />
//...
<button onClick={() => onClick(item)}>{item}</button>
The full code is as follows:
import React, { useCallback, useState } from 'react';
export const Root = () => {
const [items, setItems] = useState(['A', 'B']);
const _onClick = useCallback(item => alert(item), []);
return (
<>
<button onClick={() => setItems(['A', 'B', 'C'])}>Button</button>
{items.map((item, index) => (
<Item key={index} item={item} onClick={_onClick} />
))}
</>
);
};
const Item = React.memo(({ item, onClick }) => {
useEffect(() => {
console.log("Item: ", item);
});
return <button onClick={() => onClick(item)}>{item}</button>;
});
You were calling _onClick from the wrong place. Rather than calling on the Item component, you should call on the button's onClick event.
Check these working Code Sandbox.
Editing my question to make it a bit clearer
I don’t want the button to re-rendering when I type in the field and when I click on the button I want to update a state object
Here I have 2 components
const mainState = {
title: '',
};
const ButtonComponent = ({ confirmTitleName }) => {
return (
<>
<TestReRender label={'Button Container'}/>
<button style={{backgroundColor: 'red', outline: 'none'}} onClick={() => confirmTitleName('confirmTitleName >>>')}>CLICK ME</button>
</>
)
};
const InputComponent = ({ state, setState }) => {
return (
<>
<TestReRender label={'Input Container'}/>
<input
type="text"
value={state}
onChange={(e) => setState(e.target.value)}
/>
</>
)
};
Then I have created a component made up of the previous two
const InputAndButtonComponent = memo(({ confirmTitleName }) => {
const [state, setState] = useState('');
const Btn = () => <ButtonComponent confirmTitleName={() => confirmTitleName(state)}/>;
return (
<>
<InputComponent state={state} setState={setState} />
<Btn />
</>
)
});
The last component InputAndButtonComponent is then imported in the Main component
const Main = () => {
const [confirmTitle, setConfirmTitle] = useState(mainState);
const confirmTitleName = useCallback((value) => {
setConfirmTitle((prevState) => (
{
...prevState,
title: value
}
))
}, []);
return (
<main className={styles.CreateWorkoutContainer}>
<>
<TestReRender label={'Main Container'}/>
<div>
<InputAndButtonComponent confirmTitleName={confirmTitleName} />
</div>
</>
</main>
)
};
Now the problem is that when I write the component InputAndButtonComponent as follow it re-renders when I type in the input field
const InputAndButtonComponent = memo(({ confirmTitleName }) => {
const [state, setState] = useState('');
return (
<>
<InputComponent state={state} setState={setState} />
// This re-renders when typing
<ButtonComponent confirmTitleName={() => confirmTitleName(state)}/>;
</>
)
});
But the original version does not re-render when I type in the field
const InputAndButtonComponent = memo(({ confirmTitleName }) => {
const [state, setState] = useState('');
// This makes the <Btn /> below not re-rendering. I don't understand why
const Btn = () => <ButtonComponent confirmTitleName={() => confirmTitleName(state)}/>;
return (
<>
<InputComponent state={state} setState={setState} />
// This does not re-render
<Btn />
</>
)
});