Doesn´t render items React useState - reactjs

I would like to add new tasks but they are not rendered, here is this part of my code:
setItemsFromBackend([...itemsFromBackend, {id: uuidv4(), content: text }]);
setText("")
}
const [itemsFromBackend, setItemsFromBackend] = useState([{ id: uuidv4(), content: "First task" }]);
const [text, setText] = useState("");
const columnsFromBackend = {
[uuidv4()]: {
name: "Requested",
items: itemsFromBackend,
},
[uuidv4()]: {
name: "To do",
items: [],
},
[uuidv4()]: {
name: "In Progress",
items: [],
},
[uuidv4()]: {
name: "Done",
items: [],
},
};
<div>
<input type="text" value={text} onChange={(e) => setText(e.target.value)}/>
<button onClick={addItem}>Add</button>
</div>
Here is the complete project in codesandbox:
https://codesandbox.io/s/trello-task-yhbmu?file=/src/Kanban.jsx
Any help will be appreciated. Thanks!

setItemsFromBackend({ ...itemsFromBackend, id: uuidv4(), content: text });
itemsFromBackend is an array, so you're spreading in the wrong spot. Try this:
setItemsFromBackend([...itemsFromBackend, {id: uuidv4(), content: text }]);

First thing, instead of a div use a proper form around the form elements. And instead of listening on the button's click event, listen on form submit, so you can add an item by hitting the Enter key too, not just button click.
<form onSubmit={addItem}>
<input type="text" value={text} onChange={(e) => setText(e.target.value)} />
<button>Add</button>
</form>
I think, you should update the way you handle state. At the moment there are different pieces everywhere. You use columns, columnsFromBackend and these also contain the itemsFromBackend state. And with all these it's way too easy to mutate the state without realising it.
In your addItem method you update itemsFromBackend by using setItemsFromBackend and you forgot that you should also update the columns by using setColumns. If you don't use setColumns, React will not be aware of the changes and you won't see the updates because React won't re-render the components.
Not sure what you receive from your backend, but you should either use the columnsFromBackend object as state (which doesn't seem to be flat enough), or you could create a new state for each columns. You kind of use both of these at the moment by storing the requested column in itemsFromBackend.
The following snippet uses only columns and text states, everything unnecessary is removed, and columns is updated in addItem the same way as you do it in onDragEnd:
function Kanban() {
const [columns, setColumns] = useState({
[uuidv4()]: {
name: "Requested",
items: [{ id: uuidv4(), content: "First task" }]
},
[uuidv4()]: {
name: "To do",
items: []
},
[uuidv4()]: {
name: "In Progress",
items: []
},
[uuidv4()]: {
name: "Done",
items: []
}
});
const [text, setText] = useState("");
const addItem = (e) => {
e.preventDefault();
const item = { id: uuidv4(), content: text };
const requestedColumnId = Object.entries(columns).find(
(i) => i[1].name === "Requested"
)[0];
const column = columns[requestedColumnId];
setColumns({
...columns,
[requestedColumnId]: {
...column,
items: [...column.items, item]
}
});
setText("");
};
const onDragEnd = (result, columns, setColumns) => {
// no changes here
}
return (
<div style={{ display: "flex", justifyContent: "center", height: "100%" }}>
<form onSubmit={addItem}>
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
/>
<button type="submit">Add</button>
</form>
{/* no changes below */}
You can find the forked sandbox here: https://codesandbox.io/s/trello-task-forked-okix3

Related

How to reset state with event.target.value and make it persist on multiple <li>

I am trying to grab the user input on key pressed and pass it to that list above next. I feel like there must be a way to reset the state and make it persist, but I just can't figure it out? How can I understand this?
import { useState, useEffect, useRef, useMemo } from 'react';
import '../sign.css';
const VALUES = [
{ id: 1, label: "name", text: "Hi, What is your Name?", placeholder: "Enter your full name" },
{ id: 2, label: "uname", text: "What shall we call you?", placeholder: "Enter a username" },
{ id: 3, label: "email", text: "Enter you email", placeholder: "Email" },
{ id: 4, label: "password", text: "Choose a password", placeholder: "make sure you dont forget" },
{ id: 5, label: "signup", text: "sign up", placeholder: ""},
];
export default function SignUp() {
const [show, setShow] = useState(VALUES)
const [currentIndex, setCurrentIndex] = useState(0);
const [details, setDetails] = useState('');
useEffect(() => {
}, [show]);
const onKeyPressed = (ev, id) => {
if (ev.charCode === 13) {
ev.preventDefault();
const nextRender = currentIndex + 1;
if (nextRender < show.length) {
setCurrentIndex(nextRender);
setDetails(ev.target.value);
} else {
//todo
}
}
}
const displayItem = useMemo(() => show[currentIndex], [show, currentIndex]);
return (
<div className="container" id="container">
<div className="navigation">
<ol>
<li>{this should display their name}</li>
<li>{this should display their username}</li>
<li>{this should display their email}</li>
</ol>
</div>
<form id="sign-form" className="sign-form">
<ol className="questions">
{
<li onKeyPress={(KeyboardEvent) => onKeyPressed(KeyboardEvent, displayItem.id)} key={displayItem.id} >
<span><label htmlFor={displayItem.label}>{displayItem.text}</label></span>
<input id={displayItem.id} name={displayItem.label} type="text" placeholder={displayItem.placeholder} autoFocus/>
</li>
};
</ol>
</form>
</div>
)
Right, I think I know what you mean now. I've run the code in CodeSandbox and it makes sense. You want to do like a stepper for your registration where you ask a single question at a time.
Storing values in an object would still be a preferable way of doing this. But you need to get the label for the value and append it to the existing object. You can build your object with data when you go through your stepper.
Here is a working solution. Hopefully that's what you were looking for: https://codesandbox.io/s/unruffled-pasteur-76xux4?file=/src/App.js
I modified the onKeyPressed to grab the label from VALUES array based on the index we are currently on. Then that label is used as a key inside of the object where the value is the value from the event handler
const onKeyPressed = (ev, id) => {
if (ev.charCode === 13) {
ev.preventDefault();
const label = show[currentIndex].label; // grab the label based on the current index
const nextRender = currentIndex + 1;
if (nextRender < show.length) {
setCurrentIndex(nextRender);
setDetails({ ...details, [label]: ev.target.value }); // add the value to the details object where key is the label
} else {
//todo
}
}
};
I let you the code that it works like I think that you want
import { useState, useEffect, useRef, useMemo } from 'react';
const VALUES = [
{ id: 1, label: "name", text: "Hi, What is your Name?", placeholder: "Enter your full name" },
{ id: 2, label: "uname", text: "What shall we call you?", placeholder: "Enter a username" },
{ id: 3, label: "email", text: "Enter you email", placeholder: "Email" },
{ id: 4, label: "password", text: "Choose a password", placeholder: "make sure you dont forget" },
{ id: 5, label: "signup", text: "sign up", placeholder: ""},
];
export default function SignUp() {
const [show, setShow] = useState(VALUES)
const [currentIndex, setCurrentIndex] = useState(0);
const [details, setDetails] = useState({});
useEffect(() => {
}, [show]);
const onKeyPressed = ( ev, id ) => {
if (ev.charCode === 13) {
ev.preventDefault();
const nextRender = currentIndex + 1;
if (nextRender < show.length) {
setCurrentIndex(nextRender);
const label = VALUES[currentIndex].label;
details[label] = ev.target.value;
setDetails(details);
} else {
//todo
}
}
}
const displayItem = useMemo(() => show[currentIndex], [show, currentIndex]);
return (
<div className="container" id="container">
<div className="navigation">
<ol>
{Object.keys(details).map((key) => (
<li><a href="#" dataref={key}>{key}: {details[key]}</a></li>
))}
</ol>
</div>
<form id="sign-form" className="sign-form">
<ol className="questions">
<li onKeyPress={( KeyboardEvent ) => onKeyPressed(KeyboardEvent, displayItem.id)} key={displayItem.id}>
<span><label htmlFor={displayItem.label}>{displayItem.text}</label></span>
<input id={displayItem.id} name={displayItem.label} type="text" placeholder={displayItem.placeholder}
autoFocus/>
</li>
</ol>
</form>
</div>
)
}

How can I send filters parameters selected from Ant design Reacjs to nodejs backend?

How can I send filters parameters selected from Ant design Reacjs to nodejs backend?
I want to apply filtration. So for this purpose I make backend API which works correctly when I send data from Postman. Now I want that Filters data parameter should be send from fron-end(reactjs). How Can I send it from frontend using Ant design Tree component?
Here is my code:
// popup
const [isModalVisible, setIsModalVisible] = useState(false);
// dropdown tree states
const [expandedKeys, setExpandedKeys] = useState([]);
const [checkedKeys, setCheckedKeys] = useState([]);
const [selectedKeys, setSelectedKeys] = useState([]);
const [autoExpandParent, setAutoExpandParent] = useState(true);
const [information, setInformation] = useState([])
// handle tree's categories functions
const onExpand = (expandedKeys, info) => {
console.log("onExpand info", info)
console.log("onExpand", expandedKeys); // if not set autoExpandParent to false, if children expanded, parent can not collapse.
// or, you can remove all expanded children keys.
setExpandedKeys(expandedKeys);
setAutoExpandParent(false);
};
const onCheck = (checkedKeys, info) => {
// console.log("onCheck", checkedKeys)
console.log("onCheck info", info.node)
setCheckedKeys(checkedKeys);
setInformation((prevSelected)=>[...prevSelected, info.node])
};
const onSelect = (selectedKeys, info) => {
console.log("onSelect selectedKeys",selectedKeys)
console.log("onSelect info", info);
setSelectedKeys(selectedKeys);
};
// popup functions
const showModal = () => {
setIsModalVisible(true);
};
const handleOk = () => {
setIsModalVisible(false);
};
const handleCancel = () => {
setIsModalVisible(false);
};
const handleApplyFilter = ()=>{
console.log("Apply button ", information);
}
const treeData = [
{
title: "Shelf Life",
key: "0",
checkable: false,
children: [
{
title: "10",
key: "0-1",
checkable: true,
},
{
title: "20",
key: "0-2",
checkable: true,
}
],
}
];
return(
<div>
<div>
<Button
type="default"
size="large"
onClick={showModal}
className="filteration"
>
<FilterOutlined /> Filters
</Button>
<Modal
title="Filters"
visible={isModalVisible}
onOk={handleOk}
onCancel={handleCancel}
footer={[
<Button key="1" onClick={()=>setCheckedKeys([])}>Reset</Button>,
<Button key="2" type="primary" onClick={handleApplyFilter}>
Apply
</Button>,
]}
>
<Tree
checkable
onExpand={onExpand}
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
onCheck={onCheck}
checkedKeys={checkedKeys}
onSelect={onSelect}
selectedKeys={selectedKeys}
treeData={treeData}
/>
</Modal>
</div>
</div>
)
Here is my post request which I send from Postman
Now I want that when I click Apply button a post request is made to backend. Post request data will be similar to the data that I send from Postman.
Kindly Help me out! Thanks in Advance
In treeData, add a parent and value key in each children node/object:
const treeData = [
{
title: 'Shelf Life',
key: '0',
parent: 'shelfLife',
checkable: false,
children: [
{
title: '10',
key: '0-1',
value: 10,
parent: 'shelfLife',
checkable: true
},
{
title: '20',
key: '0-2',
value: 20,
parent: 'shelfLife',
checkable: true
}
]
},
{
title: 'Trade Name',
key: '1',
checkable: false,
children: [
{
title: 'Head & Shoulder',
value: 'Head & Shoulder',
parent: 'tradeName',
key: '1-1'
}
]
}
];
Now create a new state:
const [filter, setFilter] = useState<any>({});
When you check any checkbox, it will call onCheck function.
const onCheck = (checkedKeys, info) => {
setCheckedKeys(checkedKeys);
let parent = info.node.parent;
setFilter((prev) => {
let keys = { ...prev };
keys[parent] = info.checkedNodes.filter((item) => item.parent === parent).map((item) => item.value);
return keys;
});
};
Here each node have its parent key. In info, we have checkedNodes which have all the nodes that are marked checked. I just mapped over them and get the value from each node and assign the array to parent of that node.

How to update a state entry in react and display it's contents in input field before updating?

I'm creating a shopping cart form that can be used to add/update/delete user info. I've used react-hook-form for form submission and validation. My initial state is empty array. When user is added, objects are appended in the state array like -
state = [
{ name: 'abc', age: '23' },
{ name: 'katy', age: '12' },
];
How can update the value in state if a div row has an edit button and it displays it in an existing input box and when i click update(another button), it updates the corresponding value.
Note- name can be same hence i can't use a state.find().
One approach
const UpdateComponent = ({ id, user, setState }) => {
const [userData, setUserData] = React.useState({
id: 0,
name: "",
age: 0
});
React.useEffect(() => {
setUserData({ id: id, name: user.name, age: user.age });
}, [user, id]);
const onChange = (e) => {
setUserData((currentData) => ({
...currentData,
[e.target.name]: e.target.value
}));
};
const onSubmit = () => {
setState((currentState) =>
currentState.map((u) => (u.id === id ? userData : u))
);
};
return (
<>
<input
onChange={onChange}
name="name"
value={userData.name}
placeholder="Name"
/>
<input
onChange={onChange}
name="age"
value={userData.age}
placeholder="Age"
/>
<button onClick={onSubmit} type="button">
Update
</button>
</>
);
};
const List = () => {
const [state, setState] = React.useState([]);
React.useEffect(() => {
setState(
[
{ name: "abc", age: "23" },
{ name: "katy", age: "12" }
].map((u, i) => ({ ...u, id: i }))
);
}, []);
React.useEffect(() => {
// debug
console.log(state);
}, [state]);
return (
<div>
{state.map((user) => (
<UpdateComponent
key={user.id}
id={user.id}
user={user}
setState={setState}
/>
))}
</div>
);
};
Take a look https://codesandbox.io/s/fragrant-surf-p5cxh?file=/src/App.js
You could use the UUID package to generate the IDs:
React.useEffect(() => {
// Generates IDs when loading the data as example
// but ideally IDs are created on user creation
setState(
[
{ name: "abc", age: "23" },
{ name: "katy", age: "12" }
].map((u) => ({ ...u, id: uuidv4() }))
);
}, []);
Sandbox: https://codesandbox.io/s/unruffled-hypatia-tjzcx
But that is not very different from the initial approach with the map index id(on the component mount), that is not ideal because we are generating IDs when the component mounts, but at least they don't keep changing on each render
My initial state is empty array. When user is added, objects are appended in the state array like
For your case you could just have an ID that increments each time a user is added, or use the uuid when its added, so your data already comes with the ID

state update not affecting on view

I'm working on a basic react project, where I have react state with object and array, what I'm trying to get done here is I want to push the object to the images array, and I want to render that in component, so far I googled things I found few methods to get it done array.concat and array.push, I tried both methods it's updating the state but it's not affecting on the component
here below is the code I had tried
const [proClrData, setProClrData] = useState([
{ color: "black", price: "", qty: "", images: [] },
{ color: "white", price: "", qty: "", images: [] },
{ color: "red", price: "", qty: "", images: [] }
]);
const addImages = (key) => {
var obj = { src: "", imgdata: "" };
proClrData[key].images = proClrData[key].images.concat(obj);
console.log(proClrData);
};
below is component code
{proClrData.length >= 1 &&
proClrData?.map((clr, key) => {
return (
<div key={key}>
<p className="pt-3 ">{clr.color}</p>
<div className="form-group">
<input
label="Price"
name="price"
value={clr?.price}
type="number"
fullWidth
required
/>
</div>
<div className="form-group pt-3">
<input
label="Quantity"
name="qty"
value={clr?.qty}
type="number"
fullWidth
required
/>
</div>
<div className="text-end pt-3">
<button
variant="contained"
color="primary"
onClick={() => addImages(key)}
>
Add Images
</button>
</div>
{clr?.images?.map((img, imgkey) => {
return (
<div className="form-group pt-3" key={imgkey}>
<input
label="Image"
name="images"
value=""
accept="image/*"
type="file"
fullWidth
required
/>
</div>
);
})}
</div>
);
})}
here is codeSandbox of working code.
Issue
You are mutating your state object and then not updating state at all.
const addImages = (key) => {
var obj = { src: "", imgdata: "" };
proClrData[key].images = proClrData[key].images.concat(obj); // mutation!!
console.log(proClrData);
};
Solution
Enqueue a state update with the new image data you want in state. Use a functional state update to update from the previous state. Shallow copy the proClrData array, and for the matching element, also shallow copy it so it is a new object reference, and then also shallow copy the nested images array and append the new object.
const [proClrData, setProClrData] = useState([
{ color: "black", price: "", qty: "", images: [] },
{ color: "white", price: "", qty: "", images: [] },
{ color: "red", price: "", qty: "", images: [] }
]);
const addImages = (key) => {
const obj = { src: "", imgdata: "" };
setProClrData(data => data.map((el, i) => i === key ? {
...el,
images: [...el.images, obj],
} : el));
};
Since React state updates are asynchronously processed, you will want to move the console log into an useEffect hook to log the state after it updates and is available on the next render.
useEffect(() => {
console.log(proClrData);
}, [proClrData]);
you must call setProClrData function to update the state.
Remember that you must not mutate the state, instead you must provide a new reference (a new array in your case) and call the updater function, otherwise React will not be aware that is should re-render the component.
const [proClrData, setProClrData] = useState([
{ color: "black", price: "", qty: "", images: [] },
{ color: "white", price: "", qty: "", images: [] },
{ color: "red", price: "", qty: "", images: [] }
]);
const addImages = (key) => {
const img = { src: "", imgdata: "" };
const newProClrData = proClrData.map((obj, index) => {
return index !== key ? data : { ...obj, images: [...data.images, img] };
});
setProClrData(newProClrData);
};
You should modify proClrData accordingly and be using setProClrData in order to trigger a re-render.
Check the documentation: https://reactjs.org/docs/hooks-state.html
You update the state directly
Use the function for this:
setProClrData(prev => prev.push() // or concat)

Cant turn on switch inside Material-Table

I am trying to create material-table with switches, that onclick changes the state of the component.
Here is component with table, which has state of the parent component as a props. I pass rows variable to the material table data prop. Switch is custom rendered field. Whenever I click on it, it triggers changeRow, which finds index of row in rows variable changes it and saves into new variable. ChangeRows is then called to change the state.
The problem is, the switch is not changing. It looks like nothing is happenning, even though I can clearly see new state in console.
const StuffTable = ({rows, changeRows}) => {
const changeRow = (oldRow, e) => {
const changeData = {[e.target.name]: e.target.checked};
const newRow = {...oldRow, ...changeData};
console.log(oldRow, e);
const index = rows.findIndex(dtaRow => dtaRow.id === oldRow.id);
const newData = rows;
newData[index] = newRow;
console.log(newData);
changeRows(newData);
};
return (
<Container maxWidth="lg">
<Button onClick={() => { changeRow({id: 6}, { target: {name: 'borrowable', checked: true} }) }}>klikni</Button>
<MaterialTable
options={{
actionsColumnIndex: -1,
search: true,
exportButton: true,
exportDelimiter: ";"
}}
actions={[
{
icon: 'edit',
tooltip: 'Edit Study',
onClick: (event, rowData) => alert("Do you want to edit?")
}]}
columns={[
{ title: "Název", field: "title" },
{ title: "Stav", field: "status", render: (data) => <Chip label={data.status} color="primary" avatar={<Avatar src="/static/images/avatar/1.jpg" />} /> },
{ title: "Půjčovat", field: "borrowable", render: (data, id) => (<FormControlLabel control={<Switch checked={data.borrowable} onChange={(e) => changeRow(data, e)} name="borrowable" color="primary"/>} label={data.borrowable ? 'půjčovat' : 'nepůjčovat'} />) },
{ title: "Vidí všichni", field: "active", render: (data, id) => (<FormControlLabel control={<Switch checked={data.borrowable} onChange={(e) => changeRow(data, e)} name="borrowable" color="primary"/>} label={data.borrowable ? 'půjčovat' : 'nepůjčovat'} />) },
{ title: "Uskladněno", field: "location" },
]}
data={rows}
title="Moje věci"
/>
</Container>
);
};
export default StuffTable;
I tried to add button, which on click changes state to empty array, and table did show nothing. But when I triggered changeRow (mockup data) with this button, result was the same - no change on the switch.
import React, {useEffect, useState} from 'react';
import StuffTable from "../components/stuffTable";
let rows = [
{id:5, title: "prošívanice", borrowable: false, surname: "Baran", status: "zapůjčeno", location: "Praha" },
{id:6, title: "prošívanice 2", borrowable: false, surname: "Baran", status: "zapůjčeno", location: "Praha" },
{id:7, title: "prošívanice 3", borrowable: false, surname: "Baran", status: "zapůjčeno" , location: "Brno"}
];
Here is Parent component
const MyStuffPage = () => {
const [data, setData] = useState(rows);
return (
<div>
<StuffTable rows={data} changeRows={(data) => {setData(data); console.log("hou",data);}} />
</div>
);
};
export default MyStuffPage;
Here is Codesandbox with this problem:
https://codesandbox.io/s/festive-gould-i1jf7
You need to call onQueryChange whenever you want to render new data or state to the datatable, make these changes:
at the begining create a ref like so:
const tableRef = useRef(null);
then use it in the material table:
<MaterialTable
//add this
tableRef={tableRef}
options={{
actionsColumnIndex: -1,
search: true,
exportButton: true,
exportDelimiter: ";"
}}
then inside your changeRow function after updating the start and the necessary work add this:
tableRef.current.onQueryChange()
this will tell the table to render the new data with the correct state of the switch

Resources