React - parent passing data to child becomes undefined - javascript

I am fetching an object named Quiz. Im trying to pass a single element of Quiz (questionAnswerPair) to a child element named QuestionAnswer.
The Quiz is collected fine and each element is able to iterate through. However the integration doesn't work as intended currently though that is not the reason of my post.
The reason for my post is the QuestionAnswerPair is not being passed to the child element correctly (displayed undefined when printed in child component).
parent:
export default function Quiz() {
var passedid = 1; //need to replace this with passed quiz ID
const [quiz, setQuiz ]=useState('')
const [executedFetch, setExecutedFetch]=useState('')
useEffect(() => {
fetch(`http://XXX.XXX.XXX.XXX:8080/Quiz/${passedid}`)
.then(response => response.json())
.then(json => {
console.log(json)
setQuiz(json)
setExecutedFetch(true)
})
},[])
const [currentQuestionAnswerPair, setCurrentQuestionAnswerPair]=useState('')
const [executedIterate, setExecutedIterate]=useState('')
//this runs twice, once before the data collection and
//one after which is why it needs the conditional if
useEffect(() => {
if (executedFetch === true){
for (var i=0; i < quiz.questionAnswerPairs?.length; i++ )
{
console.log(quiz.questionAnswerPairs[i]) //prints current fine
setCurrentQuestionAnswerPair(quiz.questionAnswerPairs[i])
setExecutedIterate(true)
// make it wait till the question is answered somehow
// then store the answer within an attempt
}
//set entire attempt here
}
},[executedFetch])
const[ userSelectionData, setUserSelectionData]=useState('')
const childToParent = (userSelectionData) => {
setUserSelectionData(userSelectionData);
}
const parentToChild = () => {
setParentToChildData(currentQuestionAnswerPair);
}
if (executedIterate === true){
console.log("executedIterate " + executedIterate)
console.log(currentQuestionAnswerPair)
return (
<br/> <br/> <br/> <br/>
parent data = {userSelectionData}
<br/> <br/> <br/> <br/>
<QuestionAnswer parentToChild={currentQuestionAnswerPair} childToParent={childToParent}/>
</div>
);
}
else{
return (
<div className="App">
not displaying
</div>
);
}
}
child:
export default function QuestionAnswer({currentQuestionAnswerPair,childToParent}) {
const userSelectionData = "child data passed"
const [questionAnswer, setQuestionAnswer]=useState('')
useEffect(() => {
console.log(currentQuestionAnswerPair) // this is undefined
setQuestionAnswer(currentQuestionAnswerPair)
},[])
console.log(currentQuestionAnswerPair) // this is undefined
console.log(questionAnswer) // this is undefined
return (
<div>
<br/>
current question answer = {questionAnswer}
<br/> <br/>
current question answer pair = {currentQuestionAnswerPair}
<br/> <br/>
<Button primary onClick={() => childToParent(userSelectionData)}>Click Child</Button>
</div>
)
}
Furthermore passing dependency to the child useEffect [currentQuestionAnswerPair] doesn't change the issue
output in console:

change this line,
<QuestionAnswer parentToChild={currentQuestionAnswerPair} childToParent={childToParent}/>
to this :
<QuestionAnswer currentQuestionAnswerPair={currentQuestionAnswerPair} childToParent={childToParent}/>
As you are passing the props, parentToChild and childToParent, not currentQuestionAnswerPair and childToParent, the prop currentQuestionAnswerPair is always undefined

Related

Prevent last longer call to overrite shorter new call

I'm working with React, and in my component every time the user types a input the code calls a API.
But this API takes more time to return with few words than with bigger words.
So lets say i type "ne", and takes 7 seconds to return 100+ results, but before that 7 seconds, i wait a second and finish type "new york". That takes miliseconds, than finishes before the previus API call.
Ok, "new york" appears to me on the search, BUT now the first call finishes and overrites the last result.
How can i make that work? Without breaking any of the steps (aka, search in a click of a button stead while tiping), if this is possible
Short example of my code:
class MyComponent extends Component {
state = {
items = []
}
construtor(props) {
this.myCall = _debounce(this.myCall, 400);
}
myCall = async e => {
e.persist();
const query = _get(e, 'target.value', '');
try {
items = await myAPI(query)
this.setState({items})
} catch (error) {
console.error(error)
}
}
...
return (
<>
<input onChange={(e) => myCall(e)} />
{items.map(item => (
<p>item</p>
))}
</>
);
}
You could check that the input value hasn't changed while you awaited the response:
items = await myAPI(query)
if (query === _get(e, 'target.value', '')) {
this.setState({items})
}
Same thing with an implementation that avoids multiple _get calls:
const query = this.query = _get(e, 'target.value', '');
items = await myAPI(query)
if (query === this.query) {
this.setState({items})
}

Problems with Rendering Component in React + Typescript

I'm trying to render certain content onto my screen but currently it's not showing up. I know the function is entering the if conditional since I tested it with console logging.
Here is where I write the conditional to render a certain content.
renderContent = (): Content => {
this.props.contents.forEach((content:SomeType) => {
if (content.booleanCondition){
return <ImportantComponent contentData = {this.usefulFunction()}/>
}
}
}
And here is where I want to render everything.
renderEverything = () => {
return (
{this.renderContent()}
);
};
Alternatively, is there a way I can directly write the conditional in the renderEverything so I don't have to call the function renderContent?
If you want to render an ImportantComponent for each content element that satisfies the booleanCondition:
renderEverything = () => {
return this.props.contents.map(
content => content.booleanCondition && <ImportantComponent key={content} contentData = {this.usefulFunction()}/>
)
}
If you want to render only one ImportantComponent if any content element satisfies the booleanCondition:
renderEverything = () => {
const contentToBeShown = this.props.contents.find(content => content.booleanCondition)
if(contentToBeShown)
return <ImportantComponent contentData = {this.usefulFunction()}/>
}

Using axios doesn't rerender my React Native component

I am using hooks in React Native. This is my code:
useEffect(() => {
if (var1.length > 0 ){
let sym = [];
var1.map((items) => {
let count = 0;
state.map((stateItems) => {
if(items === stateItems.name) {
count = count + 1;
}
})
if (count === 0) {
sym.push(items)
}
});
async function getAllStates (sym) {
let stateValues = [];
await Promise.all(sym.map(obj =>
axios.get(ServerURL + "/note?name=" + obj).then(response => {
stateValues.push(response.data[0]);
})
)).then(() =>{
setNewItem(stateValues);
});
}
getAllStates (sym);
}
}, [var1]);
useEffect(() => {
let stateValues = state;
for( let count = 0 ; count < newItem.length; count++ ){
stateValues.push(newItem[count]);
}
setState(stateValues);
}, [newItem]);
This runs successfully without any errors. However, when the state is displayed as below, I am not seeing the latest value added in the state. It shows all the previous values. When I refresh the whole application, I am able to see my value added.
return (
<View style={styles.container}>
<Text style = {{color:"white"}}>
{
state.map( (item, key) =>{
return(
<Text key = {key} style = {{color:"white"}}> {item.name} </Text>
)
})
}
</Text>
</View>
);
Can someone tell me why this is happening? I want to see the data render immediately after the axios call. I am using React Native.
when i force update using :stackoverflow.com/questions/53215285/... it works fine. However, i am looking for a better fix if anyone can provide?
This should do:
useEffect(() => {
var1.forEach(async (name) => {
if (state.some(item => item.name === name)) return;
const response = await axios.get(ServerURL + "/note?name=" + name);
setState(state => [...state, response.data[0]]);
});
}, [var1]);
I still see two issues in your approach:
this code may start the same ajax request multiple times before the result of the firstz ajax-request is added to state; this also means that the result for the same name may be added multiple times to state.
for each item of var1 times each item of state, this is an O(n*m) problem or in this case basically O(n²) as m is pretty fast catching up to n. You should find a better approach here.
And I'm almost certain that [var1] is wrong here as the dependency for useEffect. But you'd need to show where this value comes from to fix that, too.

How to fix " item.item_name is null "

I want to add a search field in my dashboard and I have get api in this api have array and in the array I have objects so how I can do it in react what logic I should apply on it .
Here is the logic I apply on it but it returns error that
This is error:
TypeError: item.item_name is null
and this is logic i apply on it :
handleChange = event => {
const lowercasedFilter = this.state.SearchResult.toLowerCase();
this.state.all_items.filter(item => {
// console.log(item)
return (item.item_name.toLowerCase().indexOf(lowercasedFilter) !== -1)
});
this.setState({ SearchResult: event.target.value });
};
and here is input field :
<input placeholder="Type Keyword here ....." onChange={this.handleChange} value={SearchResult} />
Two things need to fix.
check if item_name is not null before calling toLowerCase()
filter() doesnot modify the original array so you need to store the result in a variable and then change the state.
Here is code
handleChange = event => {
const lowercasedFilter = this.state.SearchResult.toLowerCase();
let res = this.state.all_items.filter(item => {
// console.log(item)
return (item.item_name && item.item_name.toLowerCase().indexOf(lowercasedFilter) !== -1)
});
this.setState({all_items:res, SearchResult: event.target.value });
};
If you try this, does it fix your issue?
this.state.all_items.filter(item => item.item_name.toLowerCase() === lowercasedFilter);
I've updated it slightly as you only need to check direct comparison rather than using indexOf.
as per the comments in other answer and in your question desciption i think you want to implement search suggetion component,which fetch list of item from api and on keypress of seach box filter the list of item based on user input.
to do it you will need to maintan two list of data one is provided by API and another one is filttered on user input so you component state should be like
//a constructor of your component
constructor(props){
super(props);
this.state={
//i assume all_items will be initialied at the componentDidmount event by api call
all_items :
[{item_name: 'hello'},{item_name: 'humty'},{item_name: 'dumpty'},{item_name: 'world'}] ,
//use this to display suggestions by filtering the all_items
filtered_items:[],
SearchResult:'',//you should name it searchQuery not SearchResult as it hold input from search box
}
}
your event handle should be like this,if you check your code you are using searchResult from state before updating it
handleChange = event => {
/***
* your old code ,which is wrong initially this.state.SearchResult will be ''
* const lowercasedFilter = this.state.SearchResult.toLowerCase();
*/
const lowercasedFilter = event.target.value;
//filter the all_items to make suggestion
let filtered_items =this.state.all_items.filter(item => {
return (item.item_name.toLowerCase().indexOf(lowercasedFilter) !== -1)
});
if(filtered_items.length<=0){
filtered_items=this.state.all_items;
}
//now update both filtered items and searchResult,previosuly you are just filtering all items but not updating them
this.setState({ SearchResult:event.target.value,filtered_items:filtered_items });
}
your code render suggetions
get_suggestion=()=>{
if(this.state.SearchResult.length>0){
return <ul>
{this.state.filtered_items.map((item)=>{
return <li>{item.item_name}</li>
})}
</ul>
}
return null;
}
your render method should be like this
render() {
let suggestions=this.get_suggestion();
return (
<div className="App">
<input type='text' onKeyUp={this.handleChange} />
{suggestions}
</div>
);
}

React native, creating buttons in for loop

I am trying to get some data from API of eventbrite.
The data is event's name, and the name will be inserted into the list.
In render, buttons are created as many as the number of name which I got from API
I have got few questions below..
How to add information from API into array list - so I can use index, and value.
How to create buttons in forloop
e.g.
for ( var i =0; i<5; i++){
<Button
onPress={onPressLearnMore}
title="Learn More"
color="#841584"
accessibilityLabel="Learn more about this purple button"/>
} // % buttons are created.
This is my code.
export const renderButtons1 = (numOfBtns,title,site,navigated) => {
const views1 = [];
for ( var i = 0; i < numOfBtns; i++) {
views1.push(
<Button
onPress={(i) => navigate('EventsList', {
Title: title[i]
})
}
title = {title[i]}
color="#841584"
/>);
}
componentDidMount(){
return fetch('https://www.eventbriteapi.com/v3/events/search/location.address=glasgow&token=F7AWKEHKD6BW2TZKWO7N&expand=venue')
.then((response) => response.json())
.then((responseJson) => {
for(var x in responseJson.events){
this.setState({
state : this.state[Events].push(responseJson.events[x][0][0]["text"],"\n",)
});
}})
.catch((error) => {
console.error(error);
});
}
render() {
need to make buttons as many as the number of gotten name from API
}
For question 1, array form of api data will depend on how the data is structured before being sent to your application. You may have to shade more light on that.
Rendering views in a loop, try that
const renderButtons = () => {
const views = [];
for ( var i =0; i<5; i++){
views.push(
<Button
key={i}
onPress={onPressLearnMore}
title="Learn More"
color="#841584"
accessibilityLabel="Learn more about this purple button"
/>);
} // % buttons are created.
return views;
}
call renderButtons() in your render methods.

Categories