Unable to display output in React Hooks - reactjs

I have a list of items and on button click it should display more information on that particular item. For this, I have created one hook that gets the list of all the items from API and displays the list as it iterates inside a <ul> tag. (this is inside the return tag.) On each iteration it shows a button called "Details"
<button onClick ={moreInfo.bind(this, post)} >Details</button>
and when that is clicked it sends the information on that object from the list to the following code -
const moreInfo = (index) => {
console.log(index.name) //works
return ( <div>
{index.map = ( x => (
<div>{x.name}</div>
))}
</div>) //no output
}
console.log is displaying the name but nothing displays via return tag here

You are not mapping thought the array, you are just assigning map property to the index variable
Try do that, like this:
const MoreInfo = (index) => {
return (
<div>
{index.map(x => <div>{x.name}</div>)}
</div>
)
}

Related

Open modal form while retaining a fetch objects properties

const curTodos = useRef({});
const handleClickOpen = (o) => {
console.log(o);
curTodos.current = o;
setOpen(true);
};
const allTodos = todos.map((o) => {
console.log("re-render");
return (
<>
<div key={o.id} className="row">
<span>{o.name}</span>
<span>{o.id}</span>
<span>{o.email}</span>
<span>{o.task}</span>
<Button onClick={() => handleClickOpen(o)} variant="outlined">
Edit Todo
</Button>
</div>
</>
);
});
https://codesandbox.io/s/sweet-platform-du3i8x?file=/src/App.js:1593-1664
I made a different component for my modal
When I click on edit todo I want the todo form modal to contain the name and task that the row is on. Currently it just shows up as an empty input
That is,
currently:
I want:
curTodos is a reference to todo object
When I click on edit todos I want the default value to be set to the one on the rows.
Since its already rendered this wont work it just shows up as empty input.
useState(default) value runs only once on mount. Since you're using a component that does not unmount in this view, you can include an effect to update the form state.
// in FormModal
useEffect(() => {
setName(o.name)
setTask(o.task)
}, [o]);

How to access onCopy event value in React

I want to edit copied content in onCopy event. But I checked that the parameter of onCopy event is a clipboardevent. So, is there a way that i can edit content on Clipboard through clipboardevent vairiable. Codes are like
<div onCopy = {(e)=>{//edit content through variable e}}>
//something here
</div>
So, if you want to see the value that you are copying, just edit the code like this.
<div onCopy = onCopy={e => console.log(e.target.innerText)}>
the text is placed inside the "innerText" key.
For React just initialize a state using useState, where you can save the value.
const Test = () => {
const [data, setData] = useState(null)
return (
<div onCopy={e => setData(e.target.innerText)}>
Hello
</div>
)
}
For a single <div> with no child elements, you can access the copied content using:
e.target.innerText
OR
e.target.textContent
Such as:
<div onCopy={(e) => console.log(e.target.innerText)}></div>
// You can use the value like this:
function MyComponent() {
const [copiedValue, setCopiedValue] = useState("");
return (
<>
<div>{copiedValue}</div>
<div onCopy={(e) => setCopiedValue(e.target.innerText)}></div>
</>
)
}
NOTE: There are important differences between innerText and textContent if your <div> has any child nodes. One big difference is:
textContent gets the content of all elements, including <script> and <style> elements. In contrast, innerText only shows “human-readable” elements.
Source MDN Doc
I'm not getting the value of what I selected, I'm getting the entire value of the element!
Using the above event target properties, you will only be able to access the entire contents of the element being copied. If you want to copy the selected text from the user, you could do something like this:
function MyComponent() {
const [copiedValue, setCopiedValue] = useState("");
const handleOnCopy = (e) => {
const selectedText = document.getSelection(); // <-- this is different
setCopiedValue(selectedText);
};
return (
<>
<div>{copiedValue}</div>
<div onCopy={(e) => handleOnCopy(e)}></div>
</>
)
}
I'm getting undefined when accessing e.target.innerText and e.target.textContent!
This could be because you're trying to access the value of an <input> element. If you're trying to access a checkbox's boolean state use e.target.checked. For all the other <input>'s, use e.target.value

ReactJS sort by Asc/Desc not rendering sorted list from nested component

Project Overview
I am currently learning ReactJS and am creating a Pokedex app that allows the user to sort Pokemon objects by specific properties (name, type, hp, etc). The end product should have a search input to filter by pokemon name, a drop-down selector for sort criteria, and buttons for sort Ascend and sort Descend. State being tracked is searchQuery, sortBy, sortSelected, and pokeData(my data file).
Full code can be viewed here: https://github.com/julianne-vela/Pokedex-React/tree/dev
File Structure: SearchPage.js > SideBar.js > SortMenu.js
Problem
onClick button in SortMenu is not rendering sorted Pokemon list.
Expected Result: Click Asc/Desc button in SortMenu -> Pokemon list renders in sorted order based on criteria selected in drop-down and Asc/Desc button clicked.
Actual Result: Drop-down is updating state with correct sort criteria selected but button is not triggering any action when clicked.
What I've tried
Using destructured props throughout project
const {
sortBy,
sortSelected,
} = this.state
Currently passing the following props from SearchPage.js to child SideBar.js:
sortByValues={sortBy} // Array of sort criteria options stored in state
sortSelected={sortSelected} // Currently selected sort criteria from drop-down
handleSortSelected={this.handleSortSelected} // event handler to update state with currently selected sort criteria
sortAsc={this.sortAsc} // function to trigger Asc sort onClick
sortDesc={this.sortDesc} // function to trigger Asc sort onClick
SortMenu.js Code
import React, { Component } from 'react';
export default class SortMenu extends Component {
render() {
const {
sortByValues,
handleSortSelected,
sortAsc,
sortDesc,
} = this.props
const options = sortByValues.map(option => <option value={option} key={option}>{option}</option>)
return (
<aside>
{/* DropDown Sort By */}
<select className='dropdown'
onChange={handleSortSelected}>
{options}
</select>
{/* Sort Ascending/Descending Buttons */}
<button className='sortBtn' value='ascending' onClick={sortAsc}>Ascending</button>
<button className='sortBtn' value='descending' onClick={sortDesc}>Descending</button>
</aside >
)
}
}
I feel it's important to note that we are using strictly class components and therefore are not using constructor(props) in this project. Instead, I am using arrow functions throughout to implicitly bind this where needed.
To reiterate my goal: I need to dynamically render the pokemon list based on the sort criteria selected in the drop-down list as well as the ascend/descend button clicked. The list should update onClick.
I have been working on this for 3 days and have gone through so many iterations of sort functions that I can't even see straight anymore. I'm thinking this might be a simple over-sight due to the sheer amount of code I'm writing as well as the fact that this is a new language (I'm already proficient in Vanilla JS).
Any guidance here would be GREATLY appreciated as I'm really at a wall at this point. I don't know what else to try in order to render the sorted list.
Update
Also, would it be easier if I were to create a toggle button instead of two separate buttons for Asc/Desc?
Thank you!
Update 2
Currently I am rendering my pokemon objects in a module component that I'm calling within the SearchPage.js component. Here is the code for the Pokemon List:
export default class PokemonList extends Component {
render() {
const { filteredPokemon } = this.props
return (
<content className='pokemon-list float'>
{filteredPokemon.map(pokeObject =>
<PokeItem
key={pokeObject._id}
pokeImage={pokeObject.url_image}
pokeName={capFirstLetter(pokeObject.pokemon)}
pokeType={capFirstLetter(pokeObject.type_1)}
pokeHp={pokeObject.hp}
pokeAtt={pokeObject.attack}
pokeDef={pokeObject.defense}
/>)}
</content>
);
}
}
This is pulling the filtered pokemon from my filter method on SearchPage.js:
const filteredList = pokeData.filter(pokeObject => {
return pokeObject['pokemon'].includes(this.state.searchQuery) || pokeObject['type_1'].includes(tFilterSelected);
});
To sort the items, I'm using two separate arrow functions. These are housed in the first section of the SearchPage.js component (outside of render and return):
sortAsc = () => {
this.setState(prevState => {
this.state.pokeData.sort((a, b) => (a[this.state.sortSelected] - b[this.state.sortSelected]))
})
}
sortDesc = () => {
this.setState(prevState => {
this.state.pokeData.sort((a, b) => (b[this.state.sortSelected] - a[this.state.sortSelected]))
})
}
Update 3
Added project to CodeSandbox. Can view here: https://codesandbox.io/s/pokedex-react-crflb
A comparator function that subtracts the arguments would work fine for sorting numbers but would fail on strings since it would return NaN.
sortAsc = () => {
const { pokeData, sortSelected } = this.state
this.setState({
pokeData: [...pokeData].sort((a, b) => {
if (a[sortSelected] > b[sortSelected]) {
return 1
}
if (a[sortSelected] < b[sortSelected]) {
return -1
}
return 0
})
})
}
sortDesc = () => {
const { pokeData, sortSelected } = this.state
this.setState({
pokeData: [...pokeData].sort((a, b) => {
if (a[sortSelected] > b[sortSelected]) {
return -1
}
if (a[sortSelected] < b[sortSelected]) {
return 1
}
return 0
})
})
}
Working CodeSandbox

Customize Array State in ReactJs with ES6

i am fetching some data (in array) from api and assigning it to state called items. then calling an Item component for each item of the state by following code:
<div>
{items.length} results found for {search_term}
{items.map((item) =>
(item.active_price > 0 && item.show) ? <Item key={item.id} details={item} items={items} setItems={setItems}/> : ""
)}
</div>
The array looks like this:
Next plan is adding a remove button in Item component, which will change value of show to false (for the item clicked) in the items state. What I am thinking is, as the state will change, the items state will be re-rendered and the item turned to false will be hidden from view. I am adding a onCLick event listener on the button and the function is:
const handleClick = () => {
console.log(({...props.items,[props.details.number - 1]:false}))
// props.setItems(prevState=>({...prevState,[props.details.number - 1]:false}))
}
I'll call props.setItems later but for now, I am trying to see if I can just edit that element to turn the show into false. the code above replace the whole index with false.
in the onClick function, I only want to edit the value of show, not replace the entire entry. I have tried:
({...props.items,[props.details.number - 1][show]:false})
and
({...props.items,[props.details.number - 1].show:false})
It shows syntax error before [show] and .show respectively. How can I edit my handleClick function to edit the value of show properly.
Thanks.
It looks like you are converting your array into an object in your current method. (the first screen shot is an array, the second is an object). You're going to run into issues there.
To just remove the item from the array, it would be easiest just to use .filter();
const handleClick = () => {
props.setItems(
prevState => prevState.filter(item => item.number !== props.details.number)
);
}
To set the show property is a bit more complicated. This is pretty standard way to do so without mutating.
const handleClick = () => {
props.setItems(prevState => {
const itemIndex = prevState.findIndex(
item => item.number == props.details.number
);
// Using 'props.details.number - 1' will not be a valid index when removing items
return [
...prevState.slice(0, itemIndex), // Your state before the item being edited
{...prevState[itemIndex], show: false}, // The edited item
...prevState.slice(itemIndex + 1) // The rest of the state after the item
];
});
}
You can try the following syntax of code, assuming items is state here-
const [items,setItems]=.....
The following is the code
const handleClick = () => {
setItems({
...items,
show: false,
})}
It will update the items state with value show as false.

How do I update an array using useState?

I have the following scenario:
DisplayList component, which renders a list. The list record is passed in via props (this contains the list name, description, author etc), along with the initial items to be listed, and the current 'mode' (can be 'r', 'g' or 'a'). The component displays the list information, and then renders a DisplayItem component for each list item. The DisplayItem component also takes a callback function, so that when the user clicks on an item in the list, it becomes 'tagged' with the current mode (ie the 'mode' property of the item changes).
const DisplayList = (props) => {
// Get list object (contains list name etc)
// and current mode (r, a or g)
const { list, currentMode } = props
// Array of items to show
// This is an array of objects, with each object representing a single item
// eg {name: itemname, mode: r}
const [ items, setItems] = useState(props.items)
// Callback function - triggered when the rendered DisplayItem is clicked
// This changes the 'mode' of the item to the current mode
function handleItemClick(index) {
items[index].mode = currentMode
setItems(items) // At this point the list of items should re-render
}
}
return (
<div className="displayItem">
{
items.map(item => <DisplayItem key={uuid()} item={item} handleCellClick={handleItemClick} />)
}
</div>
)
}
The problem I'm having is that clicking an item seems to trigger the handleItemClick function to update the itemList, but when it gets to setItems, it doesn't re-render.
You are not passing the index to your callback.
try this:
items.map({item, index} => <DisplayItem key={uuid()} item={item} handleCellClick={() => handleItemClick(index)} />)
edited as pointed by Brian Thompson at the comments

Resources