how to get single data from state ? basically I want to post the data in firestore but i cant get that how to get all data from specific index of array ?
const [tableData, setTableData] = useState([]);
//this contain all the data but how to get single data from state?
console.log("postData", tableData);
//just like this
console.log("date" , tableData.date); //undefined
console.log("date" , tableData.description); //undefined
console.log("date" , tableData.cashAdd); //undefined
//also I do with find function but there is an another way to do it ?
const dataa = tableData.find((x)=> {
return x;
})
console.log("date", dataa.date);
//only get 1 date of a single array not an all the dates of all arrays
You need to filter out that particular data based on id from main data array.
Example:
const { useState } = React;
const data = [{
id:1,
name: 'Dan',
description:'Dab',
date: 'someDate',
cashAdd: true
},{
id:2,
name: 'John',
description:'John',
date: 'someDate',
cashAdd: false
},{
id:3,
name: 'Riya',
description:'Riya',
date: 'someDate',
cashAdd: true
}]
function App() {
const [selectedId, setSelectedId] = useState(null);
const selectedItem = data.find(item => item.id === selectedId)
return (
<div className="App">
<ul>
{data.map(item => {
return <li onClick={() => setSelectedId(item.id)}>{item.name}</li>
})}
</ul>
<h2>Selected Item</h2>
{JSON.stringify(selectedItem)}
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Related
I'm trying to filter some data by their category id. Right now I can filter by category with value: 1. What I am trying to achieve is when the button is clicked again, it should clear the filter. Here is the code below:
const onPress = () => {
const filteredResults = results.filter((result) =>{
return result.category === 1;
});
setResults(filteredResults);
};
The button:
<Button onPress={() =>{onPress()}}>Lajme</Button>
How can I implement this function in the same button:
const clearFilter = () => {
const filteredResults = results
setResults(filteredResults);
};
Store the current filter (category) with useState(), and whenever you update it (see toggleFilter) check if the current filter is already set. If it is, reset (change to null for example).
const { useState, useMemo } = React;
const Demo = ({ results }) => {
const [category, setCategory] = useState(null);
const filteredResults = useMemo(() => results.filter(result => category === null || result.category === category), [results, category]);
const toggleFilter = cat => setCategory(c => cat === c ? null : cat);
return (
<div>
<button onClick={() => toggleFilter(1)}>Cat 1</button>
<button onClick={() => toggleFilter(2)}>Cat 2</button>
<div>
{JSON.stringify(filteredResults)}
</div>
</div>
);
};
const results = [{ category: 1 }, { category: 2 }, { category: 2 }, { category: 1 }]
ReactDOM.render(
<Demo results={results} />,
root
)
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="root"></div>
Basically, you can use a state variable to know if the filter is currently applied or not.
const [isFiltered, setFiltered] = useState(false);
const onPress = () => {
if (isFiltered) {
setResults(results); // Update results without filter
setFiltered(false); // Update the state
} else {
const filteredResults = results.filter(({ category }) => category === 1); // Filter the results
setResults(filteredResults); // Update results
setFiltered(true); // Update the state
}
};
// ...
<Button onPress={onPress}>Lajme</Button>
I am a bit lost on what to do for the next step. I have managed to display the content but I can't seem to get it to filter with a click. It was easy enough to do with a different api , i followed webdevsimplified but this i can't work out and I am at my wits end!
All I want is to filter through the mapped api. for example if I check 3, it should show me only 3 starRating. Can anybody offer me some advice please.
App.js
import { useEffect, useState, useRef } from 'react'
import Header from './components/Header';
import SearchBar from './components/SearchBar';
export default function App() {
const [hotelRooms, setHotelRooms] = useState([]);
const fetchHotels = async () => {
const res = await fetch('https://obmng.dbm.guestline.net/api/hotels?collection-id=OBMNG')
const hotels = await res.json()
const hotelRooms = []
for(const hotel of hotels) {
const res = await fetch(`https://obmng.dbm.guestline.net/api/roomRates/OBMNG/${hotel.id}`)
const info = await res.json()
hotelRooms.push({ hotel, rooms: info.rooms })
}
setHotelRooms(hotelRooms)
}
useEffect(() => {
fetchHotels()
}, [])
return (
<div className="App">
<Header/>
{
hotelRooms.map(h => (
<div>
<input value={"1"} type="checkbox" onChange={}/>
<input value={"Adults"}type="checkbox" onChange={}/>
<h2> Name: {h.hotel.name}</h2>
<p> Description: {h.hotel.description}</p>
<p> Rating: {h.hotel.starRating}</p>
<p> Postcode: {h.hotel.postcode}</p>
<p> City: {h.hotel.town}</p>
<img src={h.hotel.images}/>
<p style={{ fontWeight: 'bold' }}>Rooms:</p>
{
h.rooms.map(room => (
<div>
<h5>Occupancy</h5>
<div> Adults: {room.occupancy.maxAdults}</div>
<div> Children: {room.occupancy.maxChildren}</div>
<div> Maximum guests: {room.occupancy.maxOverall}</div>
<div> Room type: {room.name}</div>
<img src={room.images}/>
</div>
))
}
</div>
))
}
</div>
);
}
You should have a state that saves the filtered properties.
const [filter, setFilter] = useState({ ratings: ["1", "2", "3", "4", "5"] });
When you show the checkboxes add a name to them and the respective values.
Remember when you use .map in render, add an unique key to the out most tag.
<div>
{["1", "2", "3", "4", "5"].map((star) => (
<div key={"input-" + star}>
<input
id={"rated" + star}
value={star}
name="ratings"
type="checkbox"
checked={filter.ratings.includes(star)}
onChange={handleRatingFilter}
/>
<label htmlFor={"rated" + star}>Rated {star} star</label>
</div>
))}
</div>
Now in the onChange handler, update the state according to the checkboxes:
const handleRatingFilter = (e) => {
if (e.target.checked) {
// adding value
const temp = [...filter.ratings];
temp.push(e.target.value);
setFilter({ ...filter, ratings: temp });
} else {
// removing value
setFilter({
...filter,
ratings: [...filter.ratings.filter((v) => v !== e.target.value)]
});
}
};
Finally, when you use .map on hotelRooms you can filter the list before mapping it.
{hotelRooms
.filter((h) => filter.ratings.includes(h.hotel.starRating))
.map((h) => (
<div key={h.hotel.name}>
stuff
</div>
))
}
Working CodeSandbox
If I am understanding your question correctly, you want it to re-render after you update hotelRooms? If this is correct, when you first render it, the value is [], a blank array. And in here :
useEffect(() => {
fetchHotels()
}, [])
That last bit [], runs once after rendering. Therefore in your case (if my assumption is correct), you will want to change it to, as you want it to re-render each time hotelRooms value change
useEffect(() => {
fetchHotels()
}, [hotelRooms])
I want to create a wizard that changes content when the user clicks a "next" button.
I'm currently trying to use the .map function, it works but how can I adjust my code to loop over each step in my array onClick?
Currently, my code just displays 3 separate inputs with all of the steps in the array, what I want to do is iterate over each step onClick.
Here is an example of my code:
Array:
const wizardControls = {
steps: [
{
step: 1,
name: 'name1',
type: 'text',
placeholder: 'name1',
},
{
step: 2,
name: 'name2',
type: 'text',
placeholder: 'name2',
},
{
step: 3,
name: 'name3',
type: 'text',
placeholder: 'name3',
},
],
};
JSX using map() function:
{steps.map((step, index) => (
<div key={index}>
<input
value={value}
name={step.name}
type={step.type}
placeholder={step.placeholder}
onChange={onChange}
/>
</div>
))}
I'm thinking the button will need a handler function to loop over the index, however, I'm unsure how to do this with the map() function.
I'm open to a better approach if the map() function isn't the best route.
One way you could do this is by slicing by which step you're on (based on index).
Here's an example of what that might look like with your code.
const [step, setStep] = useState(1)
...
steps.slice(step - 1, step).map((step, index) => (
...
))
See a working example here: https://codesandbox.io/s/pensive-northcutt-el9w6
If you want to show a step at a time, don't use Array.map() to render all of them. Use useState to hold the current index (step), and take the current item from the steps array by the index. To jump to the next step, increment the index by 1.
const { useState } = React;
const Demo = ({ steps }) => {
const [index, setIndex] = useState(0);
const [values, setValue] = useState([]);
const next = () =>
setIndex(step => step < steps.length -1 ? step + 1 : step);
const onChange = e => {
const val = e.target.value;
setValue(v => {
const state = [...v];
state[index] = val;
return state;
})
};
const step = steps[index];
return (
<div>
<input
value={values[index] || ''}
name={step.name}
type={step.type}
placeholder={step.placeholder}
onChange={onChange}
/>
<button onClick={next}>Next</button>
</div>
);
};
const wizardControls = {"steps":[{"step":1,"name":"name1","type":"text","placeholder":"name1"},{"step":2,"name":"name2","type":"text","placeholder":"name2"},{"step":3,"name":"name3","type":"text","placeholder":"name3"}]};
ReactDOM.render(
<Demo steps={wizardControls.steps} />,
root
);
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="root"></div>
In my react hooks component I am rendering data from an array of objects.
const lineInfo = [
{
id: '001',
line: 's-1125026',
arrival: '7:30',
departure: '8:00',
authorization: 'User 1',
},
{
id: '002',
line: 's-1125027',
arrival: '8:01',
departure: '8:50',
authorization: 'User 1',
},
In the .map() I'm returning data using this data:
<div>
{lineInfo.map((line) => (
<Row key={`line_${line.id}`}>
// remaining code removed for length
The list returns fine, so now I am trying to remove a row from the list.
Remove func
const [list, setList] = useState(lineInfo);
function handleRemove(id) {
console.log('id' id);
const newList = list.filter((line) => line.id !== id);
setList(newList);
}
Remove Button
<Button
className={style.close_button}
onClick={() => handleRemove(line.id)}
>
<img src={close} alt="trash" />
</Button>
</Row>
The problem I am running into is that in my console log, is that only the line.id is being removed from the array instead of the whole row of data.
How do I remove all the data belonging to a particular id?
Even though the console log shows that the text is removed, why is the text that is displayed in my row not removed from the view?
I'm not too familiar with hooks and have only been able to find examples of my particular problem with class components. Thanks in advance.
You should display the defined state with the hook. in this case the list , and not the lineInfo.
<div>
{list.map((line) => (
<Row key={`line_${line.id}`}>
// remaining code removed for length
You should not render lineInfo, render the list from local state instead:
const { useState, useCallback } = React;
const lineInfo = [
{
id: '001',
line: 's-1125026',
arrival: '7:30',
departure: '8:00',
authorization: 'User 1',
},
{
id: '002',
line: 's-1125027',
arrival: '8:01',
departure: '8:50',
authorization: 'User 1',
},
];
//make row pure component using React.memo
const Row = React.memo(function Row({ item, remove }) {
return (
<div>
<pre>{JSON.stringify(item, undefined, 2)}</pre>
<button onClick={() => remove(item.id)}>
remove
</button>
</div>
);
});
const App = () => {
const [list, setList] = useState(lineInfo);
//make remove a callback that is only created
// on App mount using useCallback with no dependencies
const remove = useCallback(
(removeId) =>
//pass callback to setList so list is not a dependency
// of this callback
setList((list) =>
list.filter(({ id }) => id !== removeId)
),
[]
);
return (
<ul>
{list.map((line) => (
<Row
key={`line_${line.id}`}
item={line}
remove={remove}
/>
))}
</ul>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
I am trying to map over an array of objects in state, conditionally returning one of two react components from that state. I then change that state at some point and would expect the component to re-render when it's object's state changed. I understand my issue is something to do with React not recognizing the change in the diff, but I'm not sure why and what pattern I need to change to in order to get this working.
Here's a codepen:
https://codepen.io/steven-harlow/pen/KKPLXRO
And the code from it:
const App = (props) => {
const [todos, setTodos] = React.useState([
{name: 'A', done: false},
{name: 'B', done: false},
{name: 'C', done: false},
])
React.useEffect(() => {
}, [todos])
const handleClick = (name) => {
const index = todos.find(todo => todo.name == name)
let tempTodos = todos;
tempTodos[index].done = true;
setTodos(tempTodos);
}
return (
<div>
<h1>Hello, world!</h1>
<div>
{todos.map(todo => {
return todo.done ? (<div key={'done' + todo.name}>{todo.name} : done</div>) : (<div onClick={() => handleClick(todo.name)} key={'notdone' + todo.name}>{todo.name} : not done</div>)
})}
</div>
</div>
)
}
Here you go, this here should work for you now. I added some notes in there.
const App = (props) => {
const [todos, setTodos] = React.useState([
{name: 'A', done: false},
{name: 'B', done: false},
{name: 'C', done: false},
])
const handleClick = (name) => {
/*
Here you were using todos.find which was returning the object. I switched
over to todos.findIndex to give you the index in the todos array.
*/
const index = todos.findIndex(todo => todo.name === name)
/*
In your code you are just setting tempTodos equal to todos. This isn't
making a copy of the original array but rather a reference. In order to create
a copy I am adding the .slice() at the end. This will create a copy.
This one used to get me all of the time.
*/
let tempTodos = todos.slice();
tempTodos[index].done = true;
setTodos(tempTodos);
}
console.log(todos)
return (
<div>
<h1>Hello, world!</h1>
<div>
{todos.map((todo,index) => {
return todo.done ? (<div key={index}>{todo.name} : done</div>) : (<div onClick={() => handleClick(todo.name)} key={index}>{todo.name} : not done</div>)
})}
</div>
</div>
)
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
Another thing I did was simplify the keys for the divs created by the map. I just added the index to the map and used that for the key, a lot cleaner that way.
Hope this helps!
React won't see the state as changed unless you create a new array.
const handleClick = n => setTodos(todos.map(t => t.name === n ? {...t, done: true} : t));