Edit element of the list with redux - javascript

I tried to find out myself how to pass value of key to the reducer but without succes. My intention is to edit on button the chosen element. For now I cant catch id of element and all the elements are changing. Could somebody tell me how it works?
my code is here:
for the container:
const mapDispatchToProps = dispatch => {
return {
onEditComponent: (component, id) => dispatch({type: actionTypes.EDIT_COMPONENT, data: {componentToReducer: component, ind: id}})
}
}
and for reducer:
case actionTypes.EDIT_COMPONENT:
return {
...state,
components: state.components.map((component,i) => i === action.data.ind ?
{...component, co: action.data.componentToReducer} : component
)
};
There is also a code when I am building a structure html:
render() {
const edit = this.props.compons.map((comp, index) =>(
<div
key={comp.id}>
<EditComponent
clicked={this.props.onEditComponent}/>
</div>
));
return (
<div>
<AddComponent
click={this.props.onAddComponent}
/>
{
this.props.compons.map((component)=>(
<div key={component.id}
>
<p
onClick={this.showTrue}
className={classes.Component}>{component.co}
</p>
<button
onClick={()=>this.props.onDeleteComponent(component.id)}>
Delete component
</button>
</div>
))
}
{this.state.show ? edit : null}
</div>
)
}

You are not passing the id and the component to onEditComponent action, pass it on to the EditComponent
const edit = this.props.compons.map((comp, index) =>(
<div
key={comp.id}>
<EditComponent
comp ={comp}
clicked={this.props.onEditComponent}/>
</div>
));
and inside EditComponent when you click on edit, you would write
handleClick= () => {
this.props.clicked(this.props.comp, this.props.comp.id)
}

So, I have a solution. EditComponent must be connected with reducer at state (mapStateToProps), and then I can pass it to this reducer to dispatch action and finally edit element from list. Thank you for attention. :)

Related

I iterate over an array with map and then try to change a boolen of an object from false to true with an onClick. UI doesn't reflect it but log does

CodeSandbox - https://codesandbox.io/s/distracted-taussig-n7e7q3?file=/src/App.js
As you can see, I iterate over the flaggers array with .map and render <div>true</div> or <div onClick={() => setToTrue(flag)}>false</div>. I assumed that if I were to click the second div, the refer property of that flag would be set to true and the component would re-render, making the div change to <div>true</div> but that doesn't seem to be the case.
In the setToTrue function I console.log the post object, and I can see that the refer property of the second flag has changed to true, but it is not shown in the UI.
import "./styles.css";
export default function App() {
const post = {
flaggers: [
{
refer: false
},
{
refer: false
}
]
}
const setToTrue = (flag) => {
flag.refer = true;
console.log(post)
}
return (
<div className="App">
{post.flaggers.map((flag) => (
<div>
{flag.refer ? <div>true</div> : <div onClick={() => setToTrue(flag)}>false</div>}
</div>
))}
</div>
);
}
Well, that's not how react you have to setState value to trigger the component to rerender which will cause to UI change try the below code it will work fine as I set a value on onClick that causes the component to rerender in short. I would suggest reading the documentation before going into the coding and know-how react works
React Documentation
export default function App() {
const [post, setPost] = React.useState([
{
refer: false,
},
{
refer: false,
},
]);
const setToTrue = (boolFlag, index) => {
const tempPost = [...post];
tempPost[index].refer = boolFlag;
setPost(tempPost);
};
return (
<div className='App'>
{post.map((flag, index) => (
<div key={`${index}flag`}>
{flag.refer ? <div onClick={() => setToTrue(false, index)}>true</div> : <div onClick={() => setToTrue(true, index)}>false</div>}
</div>
))}
</div>
);
}

Call a React Component with an onClick event

I need to call a Component (ExampleComp), and when the button is clicked, call againthe component (ExampleComp). The idea is to call the Component(ExampleComp) as many times as you press the button.
function newComponent{
<ExampleComp/>
}
------
return(
<div>
<ExampleComp/>
<Button className="btnNew" onClick=
{newComponent}> Create a new Component</Button>
</div>
)
Actually i don't know how to do it exactly and i would apreciate your help.
You can use the state for this purpose. Let's say your state is something like this:
this.state = { items: [] };
You can render all the items like the following example:
return (
<div>
{this.state.items.map(item => {
return <ExampleComp exampleProp={item.exampleProp} />;
})}
<Button className="btnNew" onClick={newComponent}>
Create a new Component
</Button>
</div>
);
And finally, you can push an item into the state, and React will take care of the rest.
function newComponent{
newItem = { exampleProp: 'Something?' };
this.setState((state, props) => ({ items: [...items, newItem] }));
}
This will do the job. I just used "exampleProp" to be an example but you don't have to use it. Actually, the state can be just a number too. The important part is using state in every user interface change.
render(){
return (
<Button className="btnNew" onClick={ this.setState({ clicked:true }) }>Create a new Component</Button>
{
this.state.clicked ? {newComponent} : null
}
)
}
This would help but though not recommended by me as setState will re-render(load) the component again onClick.

how do I output the filtered todo list in React TypeScript

It is console logging the right array out all the time, but the point here is that it should be outputting that in the 'TodoList.tsx'. Not sure how to get that fixed in this case. Anyone who could help me with this. To see the bigger picture, please click on this link:
Link to codesandbox todo
I want the returned value from App.js currentFilter function pass it to TodoListItem.js, so it will update the map function constantly when user clicks on filter buttons.
// TodoFilter
import React from 'react';
interface TodoListFilter {
currentFilter: CurrentFilter;
}
export const TodoFilter: React.FC<TodoListFilter> = ({ currentFilter }) => {
return (
<ul>
Filter
<li onClick={() => currentFilter('All')}>All</li>
<li onClick={() => currentFilter('Complete')}>Completed</li>
<li onClick={() => currentFilter('Incomplete')}>Incompleted</li>
</ul>
)
}
// App.js
const currentFilter: CurrentFilter = filterTodo => {
let activeFilter = filterTodo;
switch (activeFilter) {
case 'All':
return todos;
case 'Complete':
return todos.filter(t => t.complete);
case 'Incomplete':
return todos.filter(t => !t.complete);
default:
console.log('Default');
}
}
return (
<React.Fragment>
<TodoList
todos={todos}
toggleTodo={toggleTodo}
deleteTodo={deleteTodo}
editTodo={editTodo}
saveEditedTodo={saveEditedTodo}
getEditText={getEditText}
/>
<TodoFilter currentFilter={currentFilter}/>
<AddTodoForm addTodo={addTodo}/>
</React.Fragment>
)
// TodoListItem
import React from 'react';
import { TodoListItem } from "./TodoListItems";
interface TodoListProps {
todos: Array<Todo>;
toggleTodo: ToggleTodo;
deleteTodo: DeleteTodo;
editTodo: EditTodo;
getEditText: GetEditText;
saveEditedTodo: SaveEditedTodo;
currentFilter: CurrentFilter;
}
export const TodoList: React.FC<TodoListProps> = ({ todos, toggleTodo, deleteTodo, editTodo, getEditText, saveEditedTodo, currentFilter }) => {
return (
<ul>
{todos.map((todo, i) => {
return <TodoListItem key={i}
todo={todo}
toggleTodo={toggleTodo}
deleteTodo={deleteTodo}
editTodo={editTodo}
saveEditedTodo={saveEditedTodo}
getEditText={getEditText}
/>
})}
</ul>
)
}
//Folder structure
src
-App.tsx
-AddTodoForm.tsx
-TodoFilter.tsx
-TodoList.tsx
The reason why the list not updating is that currentFilter passed as a prop to TodoList component is not used there at all.
Please consider two ways of solving it:
Pass a full list + filter object and apply filter inside TodoList
Apply filter object on the list at App component level and pass filtered list to TodoList component.
Personally I would go with the second approach but it's up to you :)
You need to create two arrays.One is original and second is filtered like this in your example.
const [todos, setTodos] = useState(initialTodos);
const [filtered, setFiltered] = useState(initialTodos);
Now you need to send filtered array in list component.Any updation or deletion you have to make on your todos array.And in currentFilter,you have to filter from original array that is todos and set it to filtered array in like this:
useEffect(() => {
setFiltered(todos);
}, [todos]);
Link of forked sandbox : link
Let me know if this helps you.

Toggle view dynamically on click ReactJs

I have mapped list of data from JSON. When I clicked on of the item it should open a crawl with additional details from the same JSON file. I am able to map everything one I clicked bit I was not able to toggle. How do I do toggling.
This is my render method
render() {
return (
<div>
<h1>API</h1>
<div>
{this.state.apis.map(api => (
<div
key={api.id}
id={api.id}
onClick={this.handleCrawl}>
{api.title}
</div>
))}
</div>
<div>
{this.state.apis.map(api => (
<div
key={api.id}
id={api.id}>
{this.state.showCrawl[api.id] && (
<SwaggerUI url={api.opening_crawl}/>
)}
</div>
))}
</div>
</div>
);
}
This is the method for toggling. When I clicked an item the SwaggerUI component shows up and If I clicked the same link it hides.
The problem is if I clicked the 2nd link 1st link still shows. I need other view to be closed.
handleCrawl = e => {
const { id } = e.target;
this.setState(current => ({
showCrawl: { ...current.showCrawl, [id]: !current.showCrawl[id] }
}));
};
just don't spread the previous state's props.
try this:
handleCrawl = e => {
const { id } = e.target;
this.setState(current => ({
showCrawl: { [id]: !current.showCrawl[id] }
}));
};
Because in your code:
initial state:
{showCrawl: {}}
Say first time you click the first one(id: 1), your state become:
{showCrawl: {1: true}}
then u click the second one(id: 2)
{showCrawl: {1: true, 2: true}}
That's not your expected. Right?
So just don't spread the property, it should be going well.
In general, you can show or hide an element in a react component like this:
{this.state.showComponent ? (<Component/>) : (null)}
as an alternative, you can control the hiding/showing of the element in the component itself, with a show prop:
<Component show={this.state.showComponent} />
-- edit
I think I misunderstood your problem. Your problem is that you only want SwaggerUI to show for one thing at a time, but it's showing for multiple.
This is because of the way you designed your function,
handleCrawl = e => {
const { id } = e.target;
this.setState(current => ({
showCrawl: { ...current.showCrawl, [id]: !current.showCrawl[id] }
}));
};
You're only ever ADDING ids to showCrawl, not changing the ids that you toggled previously. You'll have to fix that function

Why changing state (props based) in child component can effect the props?

You can see demo here. Try to click "Edit" button and change the value of input field.
In the parent component, it pass an array of objects to its child. Inside the child component, one of objects can be passed into its state, named editedTodo. But, strangely, the prop is changed when editedTodo is changed.
This is not what I want. Anyone can help me solve this?
Here is the todo Component:
import React from "react";
export default class extends React.Component {
state = {
editedTodo: null
};
toggleEditTodo = (todo = null) => {
this.setState({ editedTodo: todo });
};
onChangeTodoText = text => {
this.setState(prevState => ({
editedTodo: Object.assign(prevState.editedTodo, { text })
}));
};
submitTodoForm = e => {
e.preventDefault();
this.props.saveEditedTodo(this.state.editedTodo);
this.setState({
editedTodo: null
});
};
render() {
const { editedTodo } = this.state;
const { todos } = this.props;
return (
<ul>
<li>{JSON.stringify(todos)}</li>
{todos.map(todo => (
<li key={todo.id}>
{todo === editedTodo ? (
<form onSubmit={this.submitTodoForm}>
<input
autoFocus
value={editedTodo.text}
onChange={e => this.onChangeTodoText(e.target.value)}
/>
<button type="submit">Save</button>
<button type="button" onClick={this.toggleEditTodo}>
Cancel
</button>
</form>
) : (
<span>
{todo.text}
<button onClick={() => this.toggleEditTodo(todo)}>Edit</button>
</span>
)}
</li>
))}
</ul>
);
}
}
https://codesandbox.io/s/3q1k4m3vm5
Here is the working version.
The problem was with this.setState({ editedTodo: todo }). You are assigning the same copy of todo from the props to the state. So it is referencing the same item. Make sure you are never mutating your props directly, it is an anti-pattern.

Categories