How to dynamically show a list of data by React? - reactjs

I am new to React and trying to display list of data with checkbox and inputbox. In details, I want to grab a series of data from database and put each record into a <div> like element with checkbox and inputbox. So I can check the record and change the data and then do the re-save action after clicking a button. Since the number of data will keep changing, how to make it dynamic? Also, how can I mark down which records are being checked and need to be saved? Thanks!
Code:
App.js:
import React from 'react';
import { useState, useEffect } from 'react';
import { Menu, Message, Button, Segment } from 'semantic-ui-react';
import SemanticDatepicker from 'react-semantic-ui-datepickers';
import 'react-semantic-ui-datepickers/dist/react-semantic-ui-datepickers.css';
import Form from './Form';
export default function App(props){
const [currentDate, setNewDate] = useState(null);
const onChange = (event, data) => setNewDate(data.value);
const loadData= (event) => {
return (<Form date = {currentDate} />);
};
return (
<div className="App">
<div>
<Menu borderless>
<Menu.Item >
<div >
<img src={logo} alt="image" />
</div>
</Menu.Item>
</Menu>
<Segment>
<SemanticDatepicker onChange={onChange} />
<Button onClick={loadData}>Load Data</Button>
</Segment>
<Segment>>
</Segment>
//Here will diaplyed returned list of data after click button
</div>
</div>
)
};
Simple JSON response:
{
"APPLE":{
"PRICE":100
},
"ORANGE":{
"PRICE":20
},
"PEAR":{
"PRICE":10
}
}

You could use your data to build your form.
You need to build the state from your data.
Also, map your input fields with respect to your state.
If the state needs different input fields, you could define your input fields in deriveStateFromData.
You can check the example here
For Object.keys, you could check the docs here
import React from 'react';
const price = {
"APPLE":{
"PRICE":100
},
"ORANGE":{
"PRICE":20
},
"PEAR":{
"PRICE":10
}
}
function deriveStateFromData(data) {
let state = {}
Object.keys(data).forEach(key => {
state[key] = data[key]['PRICE']
})
return state;
}
function MyForm({ data }) {
const [form, setFormData] = React.useState(deriveStateFromData(data));
const handleChange = e => {
setFormData({ ...form, [e.target.name]: Number(e.target.value) });
}
console.log(form)
return (
<>
{Object.keys(form).map(key => {
return (
<div>
<label>{key}</label>
<input
name={key}
value={form[key]}
onChange={handleChange}
/>
</div>
)
})}
</>
)
}
const App = () => <MyForm data={price} />
export default App;

Related

How do I transfer the value of the inputs from one page to another?

On one page I have two inputs and a button, after clicking on the button goes to the second page, how do I get data from the inputs on the second page?
navigate(path, { state: { input_value: value }}) ?
index.js
import './App.css';
function App() {
return (
<div className='App'>
<header className='App-header'>
<input type='text' placeholder='Name'/>
<input type='text' placeholder='Surname'/>
<button type='submit'>Send</button>
</header>
</div>
);
}
export default App;
getData.js
export const getData = () => {
return (
<div>
<h1>Name:</h1>
<h1>Surname:</h1>
</div>
)
};
You can have a state variable in the App component and then pass the state as a prop to GetData:
import './App.css';
import { useState, useRef } from "react";
function App() {
const nameInput = useRef(null);
const surNameInput = useRef(null);
const [fullName, setFullName] = useState({
name: "",
surName: ""
});
const sendData = () => {
// you can do some input validation here
setFullName({
name: nameInput.current.value,
surName: surNameInput.current.value,
});
}
return (
<div className='App'>
<header className='App-header'>
<input type='text' placeholder='Name'
ref={nameInput}
/>
<input type='text' placeholder='Surname'
ref={surNameInput}
/>
<button onClick={sendData}>Send</button>
</header>
<!-- some more markup here -->
</div>
);
}
export default App;
Here's how you pass your data to GetData component:
<GetData fullName={fullName} />
And then in your GetData component you get the passed props and display them:
export const GetData = (fullName) => {
return (
<div>
<h1>Name: {fullName.name}</h1>
<h1>Surname: {fullName.surName}</h1>
</div>
)
};
You can read more about hooks like useState and useRef here
So this might be Part 1 of an answer.
Taking the code you have, I've put the fields in form tag, then called handleSubmit from the built in onSubmit that is called when you click a button of type='submit' inside a form.
The values is taken from the event parameter (e) of the onSubmit (you can identify these by the 'name' attribute on the input tags) and then I am using useState hook to store the two values.
This would be where part one ends. You have the data, and you can see how it is passed to the GetDate component (the deconstructed props {name, surname} that are passed in.
From there, you should follow the documentation for your chosen router and, if you run into trouble, post the code you've tried and I can continue to help.
https://v5.reactrouter.com/web/guides/quick-start
import React, { useState } from 'react';
import { GetData } from './GetData';
export function App() {
const [theName, setTheName] = useState('');
const [theSurname, setTheSurname] = useState('');
const handleSubmit = (e) => {
setTheName(e.target.name.value);
setTheSurname(e.target.surname.value);
e.preventDefault();
}
return (
<div className='App'>
<header className='App-header'>
<form onSubmit={handleSubmit}>
<input type='text' placeholder='Name' name='name'/>
<input type='text' placeholder='Surname' name='surname'/>
<button type='submit'>Send</button>
</form>
</header>
<GetData name={theName} surname={theSurname} />
</div>
);
}
export default App;
Here is a component like your getData function.
I've added it to the App component, just so you can see the values being displayed, but for what you are looking for, you will need to read the documentation for react-router-dom
import React from "react";
export const GetData = ({name, surname}) => {
return (
<div>
<h1>Name:{name}</h1>
<h1>Surname:{surname}</h1>
</div>
)
};

Display data in modal

I would like to pass several data in a modal. My modal works fine but does not display the data I want. I would like to display the data BY ID, but I get all the data. This is my code :
App.js :
import React, { useState, useEffect } from "react";
import Modal from "./Modal";
import PlayCircleIcon from '#mui/icons-material/PlayCircle';
const App = (props) => {
const [image, setImage] = useState([])
return (
<div>
{image.map((image) => {
return (
<div key={image.id}>
<img src={...}
alt={image.title}
/>
<ExpandMoreIcon onClick={handleClick} />
</div>
)
})}
</div>
);
}
export default App;
Modal.js :
import React from 'react';
const Modal = ({ showModal, image }) => {
console.log("result", image);
return (
<div className="modal" >
<div className='modal__content'>
<h1 className='modal__title'>{image.title}</h1>
<p className='modal__description'>{image.description}</h1>
</div>
</div>
);
}
export default Modal;
I think the problem comes from here image={image} in App.js because I get 8 tables in console.log("result", movie);
{showModal && <Modal showModal={handleClick} image={image} />}
Your problem is your modal state is true/false value, so that's why once you trigger showModal, it will show all modals according to your number of images
The fix can be
Note that I modified your state name to align with new state values
const [modalImage, setModalImage] = useState(); //undefined as no image selected
const handleClick = (imageId) => {
setModalImage(imageId);
};
Your elements will be like below
<ExpandMoreIcon onClick={() => handleClick(image.id)} />
{modalImage === image.id && <Modal showModal={() => handleClick(image.id)} image={image} />}
If you want to close the modal, you can reset modalImage state
onClick={() => handleClick(undefined)}
I just noticed that you also can move your modal out of the image loop, and potentially you can pass the entire image object to the modal too
The full implementation
import React, { useState, useEffect } from "react";
import Modal from "./Modal";
import PlayCircleIcon from '#mui/icons-material/PlayCircle';
const App = (props) => {
const [image, setImage] = useState([])
const [modalImage, setModalImage] = useState(); //no image selected
const handleClick = (image) => {
setModalImage(image);
};
useEffect(() => {
...
}, []);
return (
<div>
{image.map((image) => {
return (
<div key={image.id}>
<img src={...}
alt={image.title}
/>
<ExpandMoreIcon onClick={() => handleClick(image)} />
</div>
)
})}
{modalImage && <Modal showModal={() => handleClick(modalImage)} image={modalImage} />}
</div>
);
}
export default App;
Both the map item and the image state array are named image in your code here;
image.map((image) => {
/// your code
})
which could be the ambiguity that led to showing the whole array of data instead of one image in the array.

How to highlight a JSON file with a copy option in react application?

I want to highlight a JSON file with a copy option in a JSON for the child. Child component code as below.
The below code gives an inline code highlight below. I want to show it as a code block. and users can copy the JSON file.
import React, { useEffect } from "react";
import { Card } from "antd";
import Highlight from 'react-highlight'
const ResponsesDataView = React.memo((props) => {
return (
<>
<Card size="small">
<Highlight language="json">
{JSON.stringify(props.jsonData)}
</Highlight>
</Card>
</>
);
});
export default ResponsesDataView
Beautify the the json with JSON.stringify(jsonData, null, 4)
Use textarea to display the data with readOnly and value prop.
Use useRef to handle the highlighting and copying operations.
Your ResponsesDataView will look something like this
import { useRef, memo, useCallback } from "react";
import styles from "./Json.module.css";
const ResponsesDataView = memo((props) => {
const { data } = props;
const txtarea = useRef(null);
const copyToClipboard = useCallback(() => {
txtarea.current.select();
document.execCommand("copy");
}, [txtarea]);
const selectTextAreaValue = useCallback(() => {
txtarea.current.select();
}, [txtarea]);
return (
<div className={styles.areaContainer}>
<div className={styles.btnContainer}>
<button className={styles.copyBtn} onClick={selectTextAreaValue}>
Select All
</button>
<button className={styles.copyBtn} onClick={copyToClipboard}>
Copy
</button>
</div>
<textarea
className={styles.txtArea}
ref={txtarea}
readOnly
value={JSON.stringify(data ? data : {}, null, 4)}
></textarea>
</div>
);
});
export default ResponsesDataView;
Here is a working demo

Controlling one element inside an array

I'm trying to create an edit feature to my todo-list but i'm kind of stuck and receiving a weird behaviour.
I'm filtering the array using the id's but what happens is that the entire array is changing instead of one element inside of it.
What supposed to happen is when clicking the edit button, the element im clicking on should change to an input (not the entire array)
thanks for any kind of help!
App:
import React, { useState } from "react";
import Header from "./UI/Header";
import TodoList from "./Components/TodoList";
import AddTodo from "./Components/AddTodo";
import { v4 as uuidv4 } from "uuid";
function App() {
const [todos, setTodos] = useState([]);
const [editTodo, setEditTodo] = useState(false);
const onAddHandler = (text) => {
setTodos([
...todos,
{
name: text,
id: uuidv4(),
},
]);
};
const deleteTodoHandler = (id) => {
setTodos(todos.filter((todo) => todo.id !== id));
};
const editTodoHandler = (id) => {
todos.filter((todo) => todo.id === id);
setEditTodo(!editTodo);
};
return (
<div>
<div className="App">
<AddTodo onAddHandler={onAddHandler} />
<Header />
<TodoList
todos={todos}
deleteTodoHandler={deleteTodoHandler}
editTodoHandler={editTodoHandler}
editTodo={editTodo}
/>
</div>
</div>
);
}
export default App;
TodoList.js :
import React, { useState } from "react";
import Todo from "./Todo";
const TodoList = (props) => {
return (
<Todo todo={props.todo}>
{props.todos.map((todo) => {
return (
<p>
{props.editTodo ? <input /> : <span>{todo.name}</span>}
<button onClick={() => props.deleteTodoHandler(todo.id)}>
Delete
</button>
<button onClick={() => props.editTodoHandler(todo.id)}>Edit</button>
</p>
);
})}
</Todo>
);
};
export default TodoList;
When you set the editTodo property to true, the TodoList component re-renders and loops through the todo array again, changing every <span> to an <input>. You're going to have to pass the id of the todo that you want to edit, and add a condition to only change that single item to an <input>.

Delete an item on click

I am trying to write the very first to-do application in REACT. I want to add functionality to delete to-do item when the user clicks on the delete icon. When I click on delete icon it only removes the text. Here I would like to delete the entire item. Can someone please suggest?
App.js
import './App.css';
import { useState } from 'react';
import TodoList from './TodoList';
import { v4 as uuidv4 } from 'uuid';
function App() {
const [input, setInput] = useState('');
const [todos, setTodo] = useState([]);
const addTodo = (e) => {
e.preventDefault();
const id = uuidv4();
setTodo([...todos, { id: id, text: input}])
// setTodo({todos: [...todos, input], id })
setInput('');
}
const deleteTodo = (id) => {
console.log("id" + id);
const filteredItem = todos.filter(todo => todo.id !== id);
setTodo([filteredItem]);
}
return (
<div className="App">
<form>
<input type="text" value={input} onChange={(e) => setInput(e.target.value)}/>
<button type="submit" onClick={addTodo}>Enter</button>
</form>
<TodoList todos={todos} deletetodo={deleteTodo}/>
</div>
);
}
export default App;
TodoList.js
import React from 'react'
import DeleteIcon from '#material-ui/icons/Delete';
import EditIcon from '#material-ui/icons/Edit';
const todo = ({todos, deletetodo}) => {
return (
<div>
{todos.map(todo => (
<li key={todo.id}>
{todo.text}
<div>
<DeleteIcon onClick={(todo) => deletetodo(todo.id)}/>
<EditIcon/>
</div>
</li>
))}
</div>
)
}
export default todo;
There are a few problems with your code. I will start with the most obvious. You re-render your App on EVERY change of the input field. That's just unnecessary. So insated of storing the value of the input in a state variable, I would use useRef(). So you only really need one state variable, one that stores the list of todos.
Second, your filter is correct, but then you incorrectly set the state variable with the filtered result:
const filteredItem = todos.filter(todo => todo.id !== id);
setTodo([filteredItem]);
It will already return an array and there is no need to wrap it into another one.
With those 2 main issue fixed, here is a working example along with a Sandbox:
import React, { useState } from "react";
import { v4 as uuidv4 } from "uuid";
import "./styles.css";
const TodoList = ({ todos, deletetodo }) => {
return (
<div>
{todos.map((todo) => (
<li key={todo.id}>
{todo.text}
<div>
<button onClick={() => deletetodo(todo.id)}>delete</button>
<button>edit</button>
</div>
</li>
))}
</div>
);
};
export default function App() {
const [todos, setTodo] = useState([]);
const input = React.useRef();
const addTodo = (e) => {
e.preventDefault();
const id = uuidv4();
setTodo([...todos, { id: id, text: input.current.value }]);
input.current.value = "";
};
const deleteTodo = (id) => {
setTodo(todos.filter((item) => item.id !== id));
};
return (
<div className="App">
<form>
<input ref={input} type="text" />
<button type="submit" onClick={addTodo}>
Enter
</button>
</form>
<TodoList todos={todos} deletetodo={deleteTodo} />
</div>
);
}
You have a mistake in how you're setting todo in deleteTodo:
const deleteTodo = (id) => {
console.log("id" + id);
const filteredItem = todos.filter(todo => todo.id !== id);
// Mistake! Your filteredItem is an array, you're putting your array into an array.
setTodo([filteredItem]);
}
Consequently, when you pass it further down, your component tries to get [filteredItem].text, which is undefined and React sees an empty string.
Fix:
setTodo(filteredItem);
There are multiple issues within the code:
First one is setting the values after deleting the row:
should be like this : setTodo(filteredItem);
Second issue was calling the onClick function, you already have the id with you so no need to re-call it again:
<div>
{todos.map(todoss =>
<li onClick={() => deletetodo(todoss.id)} key={todoss.id}>
{todoss.text}
</li>
)}
</div>

Resources