How can I use useEffect and store an array in Reactnative? - javascript

I have data from redux called targetFarm. targetFarm.children contains these array object data.
targetFarm.children = [
{name: 'pizza'},
{name: 'buger'},
{name: 'robster'},
{name: 'water'},
]
I want to put data in targetFarm.children into gyetong using useEffect and setgyeTong hooks.
As an array object value like below
expecter answer
gyetong = [
{'pizza'},
{'buger'},
{'robster'},
{'water'},
]
But with my code it only takes one array.
gyetong = ["pizza"]
How can i fix my code?
const App = () => {
const { targetFarm } = useAppSelector((state) => state.farm);
const [gyetong, setgyeTong] = useState([]);
return (
<Container>
{targetFarm.children.map((item, index) => {
useEffect(() => {
setgyeTong([item.name])
}, [item]);
})}
</Container>
)
}

For your information, you cannot the expected answer of gyetong you mentioned. I assume from the code, you want to have an array of array containing single item.
You can instead of this, directly do this:
const App = () => {
const { targetFarm } = useAppSelector((state) => state.farm);
const [gyetong, setgyeTong] = useState(
targetFarm.children.reduce((p, n) => {
p.push([n.name]);
return p;
}, [])
);
return <Container>{/* your code here */}</Container>;
};
By default gyetong will have the values
gyetong = [
['pizza'],
['buger'],
['robster'],
['water'],
]

Looks like very wrong expectation and usecase for this code!
gyetong = [
{'pizza'},
{'buger'},
{'robster'},
{'water'},
]
above will not possible, below possible result can be
gyetong = [
'pizza',
'buger',
'robster',
'water',
]
If we consider usecase, then there are two ways you can build your component properly,
Way 1:
const App = () => {
const { targetFarm } = useAppSelector((state) => state.farm);
return (
<Container>
{targetFarm.children.map((item) => {
return item.name;
})}
</Container>
);
};
Above is not to use extra state and render values directly.
Way 2:
const App = () => {
const { targetFarm } = useAppSelector((state) => state.farm);
const [gyetong, setgyeTong] = useState([]);
useEffect(() => {
const newGyetong = targetFarm.children.map((item) => {
return item.name;
});
setgyeTong(newGyetong);
}, [targetFarm]);
return (
<Container>
{gyetong.map((item, index) => {
return item.name;
})}
</Container>
);
};
In case you have to use state in component!
I hope this will help you! Happy coding...

You are doing set only one item at a time here
setgyeTong([item.name])
here set only one item at a time which was the last item,
you need to append that item into the array gyetong
setgyeTong({ ... gyetong, item.name });

Related

React pushing to array duplicates it instead of adding

Can anybody help me with this issue? I have a state called "filteredPokemon" which fetches a list of pokemon based on some things, then I pass it to this function called PokemonDisplayArea where I proceed to display the list of pokemon. However, when I change offset I expect the behavior to ADD onto the previous state of "Cards" which are the element rendering the HTML, however instead, it grabs the newest filteredpokemon and appends it twice. Any help would be appreciated!
Here is a video
https://clipchamp.com/watch/EcMJbOMjOaL
The code:
function PokemonDisplayArea({pokemons}) {
const [filteredPokemon, setFilteredPokemon] = useState([]);
const [offset, setOffset] = useState(20);
const [cards, setCards] = useState([]);
useEffect(() => {
let cardsSkele = [];
if (filteredPokemon.length > 0) {
for (let i = 0; i < filteredPokemon.length; i++) {
if (undefined !== filteredPokemon[i].name) {
cardsSkele.push(<PokemonCard key={filteredPokemon[i].id} name={filteredPokemon[i].name}></PokemonCard>);
cardsSkele.sort((a, b) => a.key - b.key)
}
}
setCards(prevArray => [...prevArray, ...cardsSkele]);
}
}, [filteredPokemon])
// SEARCH POKEMON RESULTS
// FILTER POKEMON RESULTS
return (
<div className="pokemon__display-area">
<GetFilteredPokemon pokemons={pokemons} amount={20} offset={offset} filteredPokemon={filteredPokemon} setFilteredPokemon={setFilteredPokemon}></GetFilteredPokemon>
{cards}
</div>
)
}
Any help would be appreciated, I am a beginner, thanks!
I tried passing different states into useEffect, and tried console logging the data but the data seems to change fine, and the list just duplicates.
For starters, I would just store data in the state rather than components itself as it easy to reason about and transform.
Iterate over the cards and update the data for existing items or add the new item. Something along these lines.
function getUpdatedCardsData(prevCards, filteredPokemon) {
// get list of all the ids for the filtered pokemons
const filteredPokemonIds = filteredPokemon.map(fp => fp.id);
// This will combine the data for existing cards that are part of
// filteredPokemon and maintain a map of ids that are new and need to be added to cards
const updatedCards = prevCards.reduce((memo, card) => {
const cardId = card.id;
const isNewCard = !filteredPokemonIds.includes(cardId);
// add it to the map if new
if(isNewCard) {
memo.newCardIds.push(cardId);
} else {
// find the card that is in filtered pokemon
const found = filteredPokemon.find(fp => fp.id === cardId);
if(found) {
const updatedCard = {
...card,
...found
};
memo.updatedCards.push(updatedCard);
}
}
return memo;
}, {
updatedCards: [],
newCardIds: []
});
}
function getNewCards(newCardIds, filteredPokemon) {
// List of the new cards
const newCards = newCardIds.reduce((memo, newCardId) => {
const found = filteredPokemon.find(fp => fp.id === newCardId);
if(found) {
memo.push(found);
}
return memo;
}, []);
}
function PokemonDisplayArea({pokemons}) {
const [filteredPokemon, setFilteredPokemon] = useState([]);
const [offset, setOffset] = useState(20);
const [cards, setCards] = useState([]);
useEffect(() => {
if(filteredPokemon) {
setCards(prevCards => {
const {
updatedCards,
newCardIds
} = getUpdatedCardsData(prevCards, filteredPokemon);
const newCards = getNewCards(newCardIds, filteredPokemon);
// add updated and new and sort them
const sortedCards = [...updatedCards, ...newCards].sort((a, b) => a.id - b.id);
return sortedCards;
});
}
}, [filteredPokemon])
// SEARCH POKEMON RESULTS
// FILTER POKEMON RESULTS
return (
<div className="pokemon__display-area">
<GetFilteredPokemon
pokemons={pokemons}
amount={20}
offset={offset}
filteredPokemon={filteredPokemon}
setFilteredPokemon={setFilteredPokemon}
/>
{cards.map((card, i) => {
const pokemon = card[i];
return (
<PokemonCard
key={pokemon.id}
name={pokemon.name}
/>
})}
</div>
)
}

I Need to fix a issue in antd tree component

what my issue is
Scenario 1: It is not opening sub-lists after searching or removing any list name from the search bar
Scenario 2: After searching any list name in the search bar that is already selected, then after searching that selected list it is showing that list but its checkbox is not selected.
so what do I need after searching list name in the search bar if that list has a sub-list for example if I New Watchlists then I want to show that sub-list also that present under this list after searching in the search bar but right it coming with empty list you can see image below..
const hasSearchTerm = (n, searchTerm) =>
n.toLowerCase().indexOf(searchTerm.toLowerCase()) !== -1;
const filterData = (arr, searchTerm) =>
arr?.filter(
(n) =>
hasSearchTerm(n.title, searchTerm) ||
filterData(n.children, searchTerm)?.length > 0
);
function filteredTreeData(data, searchString, checkedKeys, setExpandedTree) {
let keysToExpand = [];
const filteredData = searchString
? filterData(data, searchString).map((n) => {
keysToExpand.push(n.key);
return {
...n,
children: filterData(n.children, searchString, checkedKeys)
};
})
: data;
setExpandedTree([...keysToExpand]);
return filteredData;
}
const Demo = () => {
const [expandedKeys, setExpandedKeys] = useState([]);
const [checkedKeys, setCheckedKeys] = useState([]);
const [selectedKeys, setSelectedKeys] = useState([]);
const [autoExpandParent, setAutoExpandParent] = useState(true);
const [searchValue, setSearchValue] = useState("");
const [tree, setTree] = useState(treeData);
const onExpand = (expandedKeysValue) => {
console.log("onExpand", expandedKeysValue); // if not set autoExpandParent to false, if children expanded, parent can not collapse.
// or, you can remove all expanded children keys.
setExpandedKeys(expandedKeysValue);
setAutoExpandParent(false);
};
const onCheck = React.useCallback(
(checkedKeysValue, e) => {
if (e.checked) {
if (e.node?.children?.length) {
setCheckedKeys(
_.union(
checkedKeys,
_.cloneDeep([
...e.node.key,
...e.node.children.map((child) => child.key)
])
)
);
} else {
setCheckedKeys(_.union(checkedKeys, [e.node.key]));
}
} else {
if (e.node?.children?.length) {
setCheckedKeys(
_.union(
checkedKeys.filter((item) => {
return (
item !== e.node.key &&
!e.node.children.filter((child) => child.key === item).length
);
})
)
);
} else {
setCheckedKeys(
_.cloneDeep(checkedKeys.filter((item) => item !== e.node.key))
);
}
}
},
[checkedKeys, setCheckedKeys]
);
const onSelect = (selectedKeysValue, info) => {
console.log("onSelect", info);
setSelectedKeys(selectedKeysValue);
};
React.useEffect(() => {
const checked = [];
treeData.forEach((data) => {
data.children.forEach((item) => {
if (item.checked) {
checked.push(item.key);
}
});
});
setCheckedKeys(checked);
}, []);
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
React.useEffect(() => {
if (searchValue) {
const filteredData = filteredTreeData(
treeData,
searchValue,
checkedKeys,
setExpandedKeys
);
setTree([...filteredData]);
} else {
setTree(treeData);
// setExpandedKeys([]);
}
}, [searchValue, checkedKeys]);
return (
<div>
<Search
style={{ marginBottom: 8 }}
placeholder="Search"
onChange={(e) => {
setSearchValue(e.target.value);
}}
/>
<Tree
checkable
onExpand={onExpand}
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
onCheck={onCheck}
checkedKeys={checkedKeys}
onSelect={onSelect}
selectedKeys={selectedKeys}
treeData={tree}
/>
</div>
);
};
CodeSandBox Link
When you run the filterTreeData function, you assign the children to only the tree that contains the search term. So, you're searching n levels deep for the term. Notice that it works like I'd expect it to if you only search one level deep. That is, if the node has children, you return the node as such rather than filtering down all the children that don't also contain the term. when you filter with the string "new" in your example, you'll notice that none of the children in the collection "New Watchlists" contain the term and they are therefore filtered out leaving you with an empty array. My naive solution is to just return the children as in my following example:
function filteredTreeData(data, searchString, checkedKeys, setExpandedTree) {
let keysToExpand = [];
const filteredData = searchString
? filterData(data, searchString).map((n) => {
keysToExpand.push(n.key);
return {
...n,
children: n.children
};
})
: data;
setExpandedTree([...keysToExpand]);
return filteredData;
}
I call this solution naive, because I'm not exactly sure what your use case is. There might be more conditional logic you need to render in there for first searching n levels deep for the term and then and returning the whole node if the term is discovered anywhere inside the node or children.

how to display my list from store at initialization with easy-peasy?

I want to get a list of churches from the store at initialization but i can't. The log get my initial array and the new one but doesn't display. Log below:
log
here is my model:
const churchModel = {
items: [],
// ACTIONS
setAllChurches: action((state, payload) => {
state.items = payload;
}),
getInitialChurches: thunk(async (actions) => {
const { data } = await axios.post(
'http://localhost:3000/api/geo/closeto?latlong=2.3522219 48.856614&distance=10000'
);
let array = [];
const resData = data.map(async (index) => {
const res = await axios.get(`http://localhost:3000/api/institutions/all?idInstitution=${index.idInstitution}`);
array.push(res.data[0]);
});
actions.setAllChurches(array);
})
}
and my component:
const ChurchList = () => {
const classes = useStyles();
const setInitialChurches = useStoreActions(action => action.churches.getInitialChurches);
const churches = useStoreState(state => state.churches.items);
const [activeItem, setActiveItem] = React.useState(null);
useEffect(() => {
setInitialChurches()
}, []);
return (
<div className={classes.root} style={{marginTop: '20px',}}>
{ churches.map( (church) => (
<ChurchItem
key={ church.idInstitution }
church={ church }
setActiveItem={setActiveItem}
activeItem={activeItem}
/>)
), console.log(churches)}
</div>
)
};
export default ChurchList;
I tried a useEffect but nothing true. Could you help me please ?
that is not a good location to put console.log in, either put it outside the component render, inside the map or on a useEffect.
You can achieve it by using useEffect and passing churches on the array.
useEffect(() => {
// this will log everytime churches changes / initialized churches
console.log(churches);
}, [churches]);

Map through array of objects one by one in ReactJs one by one rather than listing the array

i have a react functional component
const Quiz = () => {
const [Quizdata, setQuizdata] = useState([{name: 'John'},{name: 'jack'}])
return (
{Quizdata.map(quiz => {<h1>{quiz.name}</h1>)}
)
}
In the above code i will get list of object but i want to display single element of Quizdata Array and i want to iterate through whole array by some next button
Do you maybe mean something like this..?
const App = () => {
const [Quizdata, setQuizdata] = useState([{name: 'John'}, {name: 'jack'}])
const [selected, setSelected] = useState(0)
const handleNext = () => {
setSelected(selected + 1)
}
const handlePrevious = () => {
setSelected(selected - 1)
}
return (
<div>
<button onClick={handlePrevious}>Previous</button>
<button onClick={handleNext}>Next</button>
<p>{Quizdata[selected].name}</p>
</div>
)
}

Testing child component props after parent update state using set

I have a following functional component which send some filtered data to the child component. Code is working fine i.e I can run app and see the components being render with right data. But the test I have written below is failing for ChildComponent. Instead of getting single array element with filtered value it is getting all three original values.
I am confused as similar test for FilterInputBox component for props filterValue is passing. Both tests are checking the updated props value after same event filter input change i.e handleFilterChange.
Am I missing anything? Any suggestion?
Source Code
function RootPage(props) {
const [filterValue, setFilterValue] = useState(undefined);
const [originalData, setOriginalData] = useState(undefined);
const [filteredData, setFilteredData] = useState(undefined);
const doFilter = () => {
// do something and return some value
}
const handleFilterChange = (value) => {
const filteredData = originalData && originalData.filter(doFilter);
setFilteredData(filteredData);
setFilterValue(value);
};
React.useEffect(() => {
async function fetchData() {
await myService.fetchOriginalData()
.then((res) => {
setOriginalData(res);
})
}
fetchData();
}, [props.someFlag]);
return (
<>
<FilterInputBox
filterValue={filterValue}
onChange={handleFilterChange}
/>
<ChildComponent
data={filteredData}
/>
</>
);
}
Test Code
describe('RootPage', () => {
let props,
fetchOriginalDataStub,
useEffectStub,
originalData;
const flushPromises = () => new Promise((resolve) => setImmediate(resolve));
beforeEach(() => {
originalData = [
{ name: 'Brown Fox' },
{ name: 'Lazy Dog' },
{ name: 'Mad Monkey' }
];
fetchOriginalDataStub = sinon.stub(myService, 'fetchOriginalData').resolves(originalData);
useEffectStub = sinon.stub(React, 'useEffect');
useEffectStub.onCall(0).callsFake((f) => f());
props = { ... };
});
afterEach(() => {
sinon.restore();
});
it('should send filtered data', async () => {
const renderedElement = enzyme.shallow(<RootPage {...props}/>);
const filterBoxElement = renderedElement.find(FilterInputBox);;
await flushPromises();
filterBoxElement.props().onChange('Lazy');
await flushPromises();
//This "filterValue" test is passing
const filterBoxWithNewValue = renderedElement.find(FilterInputBox);
expect(filterBoxWithNewValue.props().filterValue).to.equal('Lazy');
//This "data" test is failing
const childElement = renderedElement.find(ChildComponent);
expect(childElement.props()).to.eql({
data: [
{ name: 'Lazy Dog' }
]
});
});
});
UPDATE After putting some log statements I am seeing that when I am calling onChange originalData is coming undefined. Not sure why that is happening that seems to be the issue.
Still looking for help if anyone have any insight on this.

Categories

Resources