State is not displayed after setting it in ReactJS - reactjs

I am receiving some data as props and on click I am trying to display next items from array. In render I'm calling {this.dropdown()} which triggers folowing and display data succedsfully:
dropdown = () => {
var dropdown = undefined
if(this.props.catList){
const cat = this.props.catList
const list = JSON.stringify(this.props.catList)
if(list.length > 0){
dropdown = <div><p onClick={() =>{this.subCat()}}>{cat.title}</p>{this.state.firstSubCat}</div>
}
}
return dropdown
}
Next, when I click on item, sub categories is displayed with no issues and generates place where to display state for next function {this.state['sub'+cat.id]} :
subCat = () => {
let subCat = []
this.props.catList.children.map(cat => {
subCat.push(<div key={cat.id}><p key={cat.id} onClick={() =>{this.searchSubCat(cat.id)}}>{cat.title}</p>{this.state['sub'+cat.id]}</div>)
})
this.setState({firstSubCat: subCat})
}
Next two function is for loop through rest of array to display next items on click. (Please note that I did not use it from beginning because first line of data is not objects but contains 'children' as array so now i can use these two functions):
find = (array, id) => {
// Loop the entries at this level
for (const entry of array) {
// If we found it, return it
if (entry.id === id) {
return entry;
}
// If not but there's a type array, recursively search it
if (Array.isArray(entry.type)) {
const found = find(entry.type, id);
if (found) {
// Recursive search found it, return it
return found;
}
}
}
return undefined
}
searchSubCat = (id) => {
let subCat = []
const children = this.find(this.props.catList.children, id)
children.children.map(cat => {
subCat.push(<div key={cat.id}><p key={cat.id} onClick={() =>{this.searchSubCat(cat.id)}}>{cat.title}</p>{this.state['sub'+cat.id]}</div>)
})
this.setState({['sub' + id]: subCat})
}
So far there is no errors poping up but in generated place state is not being displayed. When generating place ( with this: {this.state['sub'+cat.id]} ) where to display state I pass its id to next step to set state with same id so state should be displayed there but nothing. If anybody can see where is issue could please respond could help me here out would be great ! Thanks.
Full code for component as requested in comment:
import React, {Component} from 'react';
class SearchResult extends Component {
constructor( props ){
super( props );
this.state = {
}
}
find = (array, id) => {
// Loop the entries at this level
for (const entry of array) {
// If we found it, return it
if (entry.id === id) {
return entry;
}
// If not but there's a type array, recursively search it
if (Array.isArray(entry.type)) {
const found = find(entry.type, id);
if (found) {
// Recursive search found it, return it
return found;
}
}
}
return undefined
}
searchSubCat = (id) => {
let subCat = []
const subId = 'sub' + id
console.log(subId)
const children = this.find(this.props.catList.children, id)
children.children.map(cat => {
subCat.push(<div key={cat.id}><p key={cat.id} onClick={() =>{this.searchSubCat(cat.id)}}>{cat.title}</p>{this.state['sub'+cat.id]}</div>)
})
this.setState({['sub' + id]: subCat})
}
subCat = () => {
let subCat = []
this.props.catList.children.map(cat => {
subCat.push(<div key={cat.id}><p key={cat.id} onClick={() =>{this.searchSubCat(cat.id)}}>{cat.title}</p>{this.state['sub'+cat.id]}</div>)
console.log('sub--'+cat.id)
})
this.setState({firstSubCat: subCat})
}
dropdown = () => {
var dropdown = undefined
if(this.props.catList){
const cat = this.props.catList
const list = JSON.stringify(this.props.catList)
if(list.length > 0){
dropdown = <div><p onClick={() =>{this.subCat()}}>{cat.title}</p>{this.state.firstSubCat}</div>
}
}
return dropdown
}
render() {
return (
<div>
{this.dropdown()}
</div>
)
}
}
export default SearchResult;
UPDATED:
I receive array from server with Redux which I send to my first component where I use map() method to find 1st level of array and send it to component with its childrens as props(catList). Cant really copy and paste catList prop value here so here is array, how i pass it and IMG of console.log(this.props.catList) :
Array:
[{"id":1,"title":"Electronics","path":"Electronics","children":[{"id":2,"title":"Accessories","children":[{"id":6,"title":"Portable Power Banks","children":[]},{"id":7,"title":"Charging Cables","children":[]},{"id":9,"title":"Batteries","children":[{"id":10,"title":"Disposable","children":[]},{"id":19,"title":"Rechargable","children":[]}]}]},{"id":3,"title":"Computers","children":[{"id":4,"title":"Components","children":[{"id":5,"title":"Laptops","children":[]}]}]}]},{"id":2,"title":"Accessories","path":"Electronics->Accessories","children":[{"id":6,"title":"Portable Power Banks","children":[]},{"id":7,"title":"Charging Cables","children":[]},{"id":9,"title":"Batteries","children":[{"id":10,"title":"Disposable","children":[]},{"id":19,"title":"Rechargable","children":[]}]}]},{"id":6,"title":"Portable Power Banks","path":"Electronics->Accessories->Portable Power Banks","children":null},{"id":7,"title":"Charging Cables","path":"Electronics->Accessories->Charging Cables","children":null},{"id":9,"title":"Batteries","path":"Electronics->Accessories->Batteries","children":[{"id":10,"title":"Disposable","children":[]},{"id":19,"title":"Rechargable","children":[]}]},{"id":10,"title":"Disposable","path":"Electronics->Accessories->Batteries->Disposable","children":null},{"id":19,"title":"Rechargable","path":"Electronics->Accessories->Batteries->Rechargable","children":null},{"id":3,"title":"Computers","path":"Electronics->Accessories->Computers","children":[{"id":4,"title":"Components","children":[{"id":5,"title":"Laptops","children":[]}]}]},{"id":4,"title":"Components","path":"Electronics->Accessories->Computers->Components","children":[{"id":5,"title":"Laptops","children":[]}]},{"id":5,"title":"Laptops","path":"Electronics->Accessories->Computers->Components->Laptops","children":null},{"id":11,"title":"Cars","path":"Cars","children":[{"id":12,"title":"Electronics","children":[{"id":13,"title":"Accessories","children":[{"id":14,"title":"Chargers","children":[]}]}]}]},{"id":12,"title":"Electronics","path":"Cars->Electronics","children":[{"id":13,"title":"Accessories","children":[{"id":14,"title":"Chargers","children":[]}]}]},{"id":13,"title":"Accessories","path":"Cars->Electronics->Accessories","children":[{"id":14,"title":"Chargers","children":[]}]},{"id":14,"title":"Chargers","path":"Cars->Electronics->Accessories->Chargers","children":null},{"id":15,"title":"DIY","path":"DIY","children":[{"id":16,"title":"Power Tools","children":[{"id":17,"title":"Accessories","children":[{"id":18,"title":"Batteries","children":[]}]}]}]},{"id":16,"title":"Power Tools","path":"DIY->Power Tools","children":[{"id":17,"title":"Accessories","children":[{"id":18,"title":"Batteries","children":[]}]}]},{"id":17,"title":"Accessories","path":"DIY->Power Tools->Accessories","children":[{"id":18,"title":"Batteries","children":[]}]},{"id":18,"title":"Batteries","path":"DIY->Power Tools->Accessories->Batteries","children":null}]
and here i use map() method from where prop catList is passed to component:
this.props.searchRes.map(cat => {
if(!cat.path.includes('->')){
categories.push(<SearchResult filtered={false} title={cat.title} id={cat.id} catList={cat} key={cat.id}/>)
}
})

Related

React rendering string after function with logic/looping

Hi I want to render a string with looping logic behind it so I decided to put a function that will return the string
function Leasing(){
let {idLeasingExact} = useParams()
const checkParam = () =>{
//return(idLeasingExact)
dropdownItems.map((item,index) => {
if(idLeasingExact == item.path){
console.log(idLeasingExact)
console.log(item.path)
console.log(item.title)
return(
item.title
)
}
})
}
return(
<div>
<h1>
{idLeasingExact ? checkParam() : "Leasing"
}
</h1>
</div>
)
}
export default Leasing;
here is the dropdown item
export const dropdownItems = [
{
title:'SMF',
path:'1',
cName:'dropdown-link'
},
{
title:'BFI',
path:'2',
cName:'dropdown-link'
},
{
title:'ADIRA',
path:'3',
cName:'dropdown-link'
}
]
I use param and that param will be used in function checkParam to return the result
the checkParam() should return the title(SMF BFI ADIRA) as the result
for example, if it's leasing/1
it should've return the title of SMF
or if it's leasing/2
it should've returned the title of BFI
but it returns null,
although the console log on the browser shows the right item.title just like the example
help appreciated I'm stuck here thanks
So you're not wanting to actually do a map. You gotta find the item on dropdownItems with path equals to idLeasingExact. Try changing your checkParam() function to something like this:
const checkParam = () => {
const item = dropdownItems.find(x => x.path == idLeasingExact);
if (item) return item.title;
else {
// do whatever you want if path is not found, for example:
return 'No title found.'
}
}
What your code is doing, is some sort of a conditional mapping and the function checkParam() is not actually returning something if you take a close look (the function inside the map does return the .title, not the checkParam()!)
map() returns a list. checkParam() is not returning that list. Just add return to the function-
const checkParam = () =>{
return dropdownItems.map((item,index) => {
if(idLeasingExact == item.path){
return(
item.title
)
}
})
}
Also, you can add your JSX logic in checkParam itself like this-
const checkParam = () => {
return dropdownItems.map((item, index) => {
if (idLeasingExact == item.path) {
return <h1>{item.title}</h1>
} else {
return <h2>{"Leasing"}</h2>
}
})
}
return <div>{checkParam()}</div>
This will give you more control based on idLeasingExact value (e.g. conditional styling)

Child components don't reflect the changes made to the parent component state

I have 3 components. The main one has Decks state that contains a list of decks with words and a handleDecks function with setDecks method. I pass the function and a chosen deck from Decks to a child component. That component filters through the deck's words and passes them down to the third component which is supposed to display the filtered words. The app throws an error because the 3rd component have nothing to display. When I tried debugging the app with console.logs I found out that the second component renders twice and its useEffect() only runs after the words array has been passed down.
The middle component calls the API to update the database. It also makes sure that the app displays up to ten new words to the user.
In there If I delete the filter method from words array
initialization or move it into the useEffect() the app seems to work but I want the 3rd component to get the filtered array right away and useEffect() doesn't run immediately on render.(UPDATE: I decided to move the filter method to the useEffect())
Is there a problem with how I'm trying to manage the state?
1st component:
const [deck, setChosenDeck] = useState(1)
const [decks, setDecks] = useState([])
useEffect(() => {
function callAPI() {
axios
.get(`http://localhost:8080/deck`)
.then(response => {
const body = response.data;
setDecks(body);
})
}
callAPI()
}, [])
function handleDecksChange(newWord) {
let newDecks = JSON.parse(JSON.stringify(decks))
let newDeck = newDecks.find(deck => deck.id == chosenDeck)
newDeck = newDeck.words.map(d => d.id == newWord.id ? newWord : d)
setDecks(newDecks)
}
return (<AppPage handleDecksChange={handleDecksChange} deck={decks.find(x => x.id === chosenDeck)} />)
2nd component:
async function callAPI(id, wordGroup) {
await axios.put(`http://localhost:8080/word/${id}/${wordGroup}`)
}
export default function LearnNewApp({ deck, handleShowAppChange, handleDecksChange }) {
let words = deck.words
useEffect(() => {
words = words.filter(x => x.wordGroup == "newLearning")
let unsubscribed = false;
if (words.length < 10) {
let vacant = 10 - words.length
let newDeck = JSON.parse(JSON.stringify(deck))
let unseenWords = newDeck.words.filter(x => x.wordGroup === "newUnseen")
for (let i = 0; i < vacant && i < unseenWords.length; i++) {
let wordUnseenToLearning = unseenWords[i]
wordUnseenToLearning.wordGroup = "newLearning"
callAPI(wordUnseenToLearning.id, "newLearning")
if (!unsubscribed) handleDecksChange(wordUnseenToLearning)
}
}
return () => unsubscribed = true;
}, [])
function memorized(word) {
callAPI(word.id, "first")
let wordLearningToFirst = {
...word, wordGroup: "first"
}
handleDecksChange(wordLearningToFirst)
}
function showAgain() {}
if (words != []) return (
<CardPage words={words} handleShowAppChange={handleShowAppChange} leftButtonFunc={memorized} rightButtonFunc={showAgain} />
)
}
3rd component:
export default function CardPage({ words, handleShowAppChange,leftButtonFunc, rightButtonFunc }) {
const [currentWord, setCurrentWord] = useState(words[0])
const [showDefinition, setShowDefinition] = useState(false)
return (
<Card>
<Card.Body>
{
showDefinition ?
<>
<h4>{currentWord.definition}</h4>
<i>{currentWord.example}</i>
</>
:
<Alert onClick={() => setShowDefinition(showDefinition => !showDefinition)}>{" "}</Alert>
}
</Card.Body>
<Card.Footer>
<div>
<Button onClick={() => leftButtonFunc(currentWord)}>show later</Button>
<Button onClick={() => rightButtonFunc(currentWord)}>
show again
</Button>
</div>
</Card.Footer>
</Card>
)
}
UPDATE:
updated the components' code, the problem persists. Still the state doesn't update when it's needed. The app is not responsive. When I am clicking on a button in the 3rd component the db updates but there's no effect on UI.

React: Update array entry with button click

I would like to have a code which updates the first entry in my array with a button click.
Does anyone have an idea what the solution could be?
Many thanks in advance,
Nicolas
I already tried the code attached.
class JobBuilder extends Component {
state = {
listings: {
accepted: [0,0,0,0,0,0,0,0,0,0]
}
}
acceptedHandler = () => {
const updatedCount = 1;
const updatedAccepted = {
...this.state.listings.accepted
}
updatedAccepted[1] = updatedCount;
}
render () {
return (
<Aux>
<JobList
listings={this.state.listings}
<button onClick={this.acceptedHandler}>Accept</button>
</Aux >
);
}
}
The spread operator for arrays is []
updatedAccepted[1] will update the second entry instead of the first one.
Have a look at below code :
acceptedHandler = () => {
const updatedCount = 1;
const clonedListing = {...this.state.listings};
const updatedAccepted = [...clonedListing.accepted]
updatedAccepted[0] = updatedCount;
this.setState({
listings: {
...clonedListing,
accepted: updatedAccepted
}
});
}
Working stackblitz

Iterating through array gives .map is not a function error in reactjs

I want to iterate through each element in the array and display it in the breadcrumb navigation.
What i am trying to do?
from a particular path or location say /list/item_id and if the item has certain information my breadcrumb navigation should change to the hierarchy of information.
For example, say i have the information of the item stored in item_information...and it is array of objects as below,
const item_information = [
{
name: "c_name",
},
{
name: "a_name",
},
{
name: "name",
}
I want to retreive only the name of each object and store it in variable display and want to display that in the breadcrumb navigation....so to loop through each name value from the variable display i use .map function. In doing so , i get an error .map is not a function.
Below is the code,
class Crumb extends React.PureComponent {
render = () => {
const link = this.props.link;
let display;
let match;
let after_link;
if (link === '/') {
display = 'Home';
} else if (match = link.match(/^\/list\/new$/)) {
display = 'new item';
} else if (match = link.match(/^\/list\/([^/]+)$/))
if (this.props.item_information > 0) {
display = this.props.item_information.map((el) => {
return el.name;
});
} else {
const model_id = match[1];
const model = this.props.models && this.props.models.find(model
=> '' + model.id === model_id);
display = itemname;
after_link = 'after_link';
}
}
//last part of the link
if (!display) {
const parts = link.split('/');
display = parts[parts.length - 1];
}
return (
<div>
{Array.isArray(display) && display.map((display) => {
return (
<div className="crumb">
<Link to={link}>{display}</Link>
</div>
);
})}
<div className="crumb">
<Link to={link}>{display}</Link>
</div>
{after_link}</div>
);
};
}
class Breadcrumb extends React.PureComponent {
render = () => {
const path = this.props.location.pathname;
const crumbs = [];
path.split('/').slice(1).forEach((part, index, parts) => {
crumbs.push('/' + parts.slice(0, index + 1).join('/'));
});
return (
<div className="breadcrumb">
{crumbs.map((link, i) => {
return (
<Fragment key={link}>
<Crumb
item_information={this.props.item_information}/>
</Fragment>);
})}
</div>
);
};
}
Could someone help me in getting rid off the error .map is not a function. thanks.

React es6, Remove item from array, by changing props

I’m new to React and right now I’m working on a project where a user should be able to choose a base ingredient, and it gets added to an array. By clicking on another base ingredient the first one should be removed from the array. Right now the chosen ingredient only removes when clicking on the same one.
I want it to be removed when clicking on another one. Please help :)
import React from 'react';
import Actions from '../../../actions/actions';
import BaseIngredientButton from './BaseIngredientButton';
class BaseIngredientItem extends React.Component {
_OnClick (props) {
if (this.props.isChosen) {
Actions.removeItem(this.props.baseIngredient);
} else {
Actions.addItem(this.props.baseIngredient)
Actions.setBaseIngredient( this.props.baseIngredient );
}
console.log(this.props.isChosen)
}
render () {
return (
<BaseIngredientButton isChosen={this.props.isChosen} onClick={ this._OnClick.bind(this)} txt={ this.props.baseIngredient.name } />
)
}
}
BaseIngredientItem.propTypes = {
baseIngredient: React.PropTypes.object.isRequired,
isChosen: React.PropTypes.bool
}
export default BaseIngredientItem;
here is my store.js
let _cart = [];
const _removeItem = ( item ) => {
_cart.splice( _cart.findIndex( i => i === item ), 1 );
console.log("Ingredients in cart after removal", _cart);
};
const _getItemInCart = ( item ) => {
return _cart.find( ingredient => ingredient.name === item.name )
};
const _addItem = ( item ) => {
if (!_getItemInCart( item )) {
_cart.push(item);
}
console.log("Ingredients in cart after addition", _cart);
};
let _currentBaseIngredient = {};
const _setCurrentBaseIngredient = ( baseIngredient ) => {
_currentBaseIngredient = baseIngredient
};
here is my action.js
addItem( item ){
dispatch({
actionType: AppConstants.ADD_ITEM,
item
})
},
removeItem( item ){
dispatch({
actionType: AppConstants.REMOVE_ITEM,
item
})
},
setBaseIngredient( baseIngredient ){
dispatch({
actionType: AppConstants.SET_BASE_INGREDIENT,
baseIngredient
})
},
Your BaseIngredientItem component has no knowledge of whether there is another base ingredient in the array, so as I mentioned in the comment, this would definitely be something to inspect at the Store level.
Is there any way to determine whether an item is of type base? If there is, you can check for its presence in your addItem function:
(please don't mind some of the psuedo-code)
const _addItem = ( item ) => {
if (item is a base ingredient)
removeCurrentBaseIngredient()
if (!_getItemInCart( item )) {
_cart.push(item);
}
console.log("Ingredients in cart after addition", _cart);
};
const removeCurrentBaseIngredient = () => {
_removeItem( _currentBaseIngredient );
};
Since the store already knows about the _currentBaseIngredient, you should be able to look it up pretty easily and call _removeItem to remove it from the _cart.
I hope that helps!

Resources