I want to render the text 'Results!' and the name of the largestLikeResult from my getLargest() function.
getLargest() {
var largestLikeResult = null
var largerstLikeNum= 0
if(this.props.results!=null){
//.map goes through every result
this.props.results.map(i=> {
console.log(i.name)
this.state.resultsRef.child(this.replaceAll("."," ",i.name)).once('value',function(snapshot) {
if(largerstLikeNum<snapshot.val().right)
{
console.log("new largest in town")
largerstLikeNum = snapshot.val().right
largestLikeResult= i.name
console.log(largestLikeResult)
}
})
})
return (
<div>
{largestLikeResult}
</div>
)
}
else {
return null
}
}
render(){
return (
<div>
Results!
<h1>{this.getLargest()}</h1>
</div>
)
}
}
export default DisplayResults
Currently, only Results! shows up on page and the name of the largestLikeResult shows up in the console, not page. Any quick changes I can add to render() to show the value of largestLikeResult?
Thanks in advance!
For a quick change, I think if you change from map to forEach it will work fine:
this.props.results.forEach(i => {
But I'd suggest to refactor getLargest() function into something similar to this:
getLargest() {
let largerstLikeNum = 0;
const { results } = this.props;
const { resultsRef } = this.state;
// Always return early
if (!results || !Array.isArray(results)) {
return null;
}
return (
<div>
{
results.map(i => {
return resultsRef.child(this.replaceAll('.', ' ', i.name)).once('value', snapshot => {
if (largerstLikeNum < snapshot.val().right) {
console.log('new largest in town');
largerstLikeNum = snapshot.val().right ;
return (
<div>{i.name}</div>
);
}
return null;
})
})
}
</div>
);
}
Related
I'm working on a trivia app using ReactjS and every time I try to update my state named "game", I get the following error message:
Uncaught TypeError: game is undefined.
My webapp is structured as follows:
App -> Question -> Answers.
in App:
const [game, setGame] = React.useState([]);
function holdAnswer(qKey) {
console.log(qKey);
setGame((oldGame) => {
oldGame.map((element) => {
return qKey === element.key ? {} : element;
});
});
console.log(qKey);
}
React.useEffect(function () {
console.log("Effect ran");
fetch("https://opentdb.com/api.php?amount=5")
.then((res) => res.json())
.then((data) =>
setGame(
data.results.map(function (element) {
return {
...element,
key: uniqid(),
answers: arrayShuffle([
...element.incorrect_answers.map(
(x) => new AnswerObj(x, false, uniqid())
),
new AnswerObj(element.correct_answer, false, uniqid()),
]),
};
})
)
);
console.log(game);
}, []);
var wholeQ = game.map((element) => {
return (
<Question wQ={element} holdAnswer={() => holdAnswer(element.key)} />
);
});
in Question Component:
export default function Question(props) {
const answers = arrayShuffle(props.wQ.answers).map((element) => {
return <Answers wholeAnswer={element} holdAnswer={props.holdAnswer} />;
});
return (
<div className="question">
<p>{decode(props.wQ.question)}</p>
<div className="answer-buttons-wrapper">{answers}</div>
</div>
);
}
in Answers Component:
export default function Answers(props) {
return (
<button className="answer-button" onClick={props.holdAnswer}>
{decode(props.wholeAnswer.answer)}
</button>
);
}
I believe the problem lies in the following block:
function holdAnswer(qKey) {
console.log(qKey);
setGame((oldGame) => {
oldGame.map((element) => {
return qKey === element.key ? {} : element;
});
});
console.log(qKey);
}
As setGame ends up not returning anything and therefor sets the state with an undefined value.
To address this we can remove the curly-braces in setGame in-order to make it an "implicit return".
Alternatively, we can add a return statement before the mapping function.
// Either this ->
setGame((oldGame) =>
oldGame.map((element) => {
return qKey === element.key ? {} : element;
});
);
// Or this ->
setGame((oldGame) => {
return oldGame.map((element) => {
return qKey === element.key ? {} : element;
});
});
I am trying to conditionally render a component based on toggling of flag inside state. It looks like the state is getting updated but the component is not getting rendered. Can some one tell what is wring here. renderTree function updates the state, but render is not called then.
import React from "react";
import CheckboxTree from "react-checkbox-tree";
import "react-checkbox-tree/lib/react-checkbox-tree.css";
import { build } from "../data";
import { Input, Dropdown } from "semantic-ui-react";
import _ from "lodash";
class Widget extends React.Component {
constructor(props) {
super(props);
this.state = {
nodes: build(),
checked: [],
expanded: [],
isDropdownExpanded: false,
keyword: ""
};
}
onCheck = checked => {
this.setState({ checked }, () => {
console.log(this.state.checked);
});
};
onExpand = expanded => {
this.setState({ expanded }, () => {
console.log(this.state.expanded);
});
};
renderTree = () => {
this.setState(
prevState => {
return {
...prevState,
isDropdownExpanded: !prevState.isDropdownExpanded
};
},
() => {
console.log(this.state);
}
);
};
onSearchInputChange = (event, data, searchedNodes) => {
this.setState(prevState => {
if (prevState.keyword.trim() && !data.value.trim()) {
return {
expanded: [],
keyword: data.value
};
}
return {
expanded: this.getAllValuesFromNodes(searchedNodes, true),
keyword: data.value
};
});
};
shouldComponentUpdate(nextProps, nextState) {
if (this.state.keyword !== nextState.keyword) {
return true;
}
if (!_.isEqual(this.state.checked, nextState.checked)) {
return true;
}
if (_.isEqual(this.state.expanded, nextState.expanded)) {
return false;
}
return true;
}
getAllValuesFromNodes = (nodes, firstLevel) => {
if (firstLevel) {
const values = [];
for (let n of nodes) {
values.push(n.value);
if (n.children) {
values.push(...this.getAllValuesFromNodes(n.children, false));
}
}
return values;
} else {
const values = [];
for (let n of nodes) {
values.push(n.value);
if (n.children) {
values.push(...this.getAllValuesFromNodes(n.children, false));
}
}
return values;
}
};
keywordFilter = (nodes, keyword) => {
let newNodes = [];
for (let n of nodes) {
if (n.children) {
const nextNodes = this.keywordFilter(n.children, keyword);
if (nextNodes.length > 0) {
n.children = nextNodes;
} else if (n.label.toLowerCase().includes(keyword.toLowerCase())) {
n.children = nextNodes.length > 0 ? nextNodes : [];
}
if (
nextNodes.length > 0 ||
n.label.toLowerCase().includes(keyword.toLowerCase())
) {
n.label = this.getHighlightText(n.label, keyword);
newNodes.push(n);
}
} else {
if (n.label.toLowerCase().includes(keyword.toLowerCase())) {
n.label = this.getHighlightText(n.label, keyword);
newNodes.push(n);
}
}
}
return newNodes;
};
getHighlightText = (text, keyword) => {
const startIndex = text.indexOf(keyword);
return startIndex !== -1 ? (
<span>
{text.substring(0, startIndex)}
<span style={{ color: "red" }}>
{text.substring(startIndex, startIndex + keyword.length)}
</span>
{text.substring(startIndex + keyword.length)}
</span>
) : (
<span>{text}</span>
);
};
render() {
const { checked, expanded, nodes, isDropdownExpanded } = this.state;
let searchedNodes = this.state.keyword.trim()
? this.keywordFilter(_.cloneDeep(nodes), this.state.keyword)
: nodes;
return (
<div>
<Dropdown fluid selection options={[]} onClick={this.renderTree} />
{isDropdownExpanded && (
<div>
<Input
style={{ marginBottom: "20px" }}
fluid
icon="search"
placeholder="Search"
iconPosition="left"
onChange={(event, data) => {
this.onSearchInputChange(event, data, searchedNodes);
}}
/>
<CheckboxTree
nodes={searchedNodes}
checked={checked}
expanded={expanded}
onCheck={this.onCheck}
onExpand={this.onExpand}
showNodeIcon={true}
/>
</div>
)}
</div>
);
}
}
export default Widget;
Problem is in your shouldComponentUpdate method:
shouldComponentUpdate(nextProps, nextState) {
if (this.state.keyword !== nextState.keyword) {
return true;
}
if (!_.isEqual(this.state.checked, nextState.checked)) {
return true;
}
if (_.isEqual(this.state.expanded, nextState.expanded)) {
return false;
}
return true;
}
Since renderTree only changes isDropdownExpanded value, shouldComponentUpdate always returns false
If shouldComponenetUpdate returns true then your component re-renders, otherwise it dosen't.
In your code sandbox, it can be seen that every time you click on the dropdown, the shouldComponenetUpdate returns false for this condition
if (_.isEqual(this.state.expanded, nextState.expanded)) {
return false;
}
Either you need to change the state of this variable in your renderTree function or you need to re-write this condition as
if (_.isEqual(this.state.isDropdownExpanded, nextState.isDropdownExpanded)) {
return false;
}
Ciao, to force a re-render in React you have to use shouldComponentUpdate(nextProps, nextState) function. Something like:
shouldComponentUpdate(nextProps, nextState) {
return this.state.isDropdownExpanded !== nextState.isDropdownExpanded;
}
When you change isDropdownExpanded value, shouldComponentUpdate will be triggered and in case return is equal to true, component will be re-rendered. Here working example (based on your codesandbox).
I'm getting an error that i'm missing key prop for my map iteration.Got confused where i'm missing one . I have a map inside map.Could you please help me
displayData() {
const { data, index } = this.state;
let sortedData = data[index].settings.map((item, id) => {
const { _init_ } = item.settings;
return _init_.map((message, index) => {
const { message_content } = message;
return message_content === undefined ? null : (
<>
<div>
<div key={index} className="settings-message">
{message_content}
</div>
</div>
<div>yes</div>
</>
);
});
});
return sortedData;
}
The key should be on the parent div.
return message_content === undefined ? null : (
<div key={index}>
<div className="settings-message">
{message_content}
</div>
</div>
)
Your top level component needs a unique key. Use explicit fragment syntax and add the key to the fragment,
displayData() {
const { data, index } = this.state;
let sortedData = data[index].settings.map((item, id) => {
const { _init_ } = item.settings;
return _init_.map((message, index) => {
const { message_content } = message;
return message_content === undefined ? null : (
<React.Fragment key={index}>
<div>
<div className="settings-message">{message_content}</div>
</div>
<div>yes</div>
</React.Fragment>
);
});
});
return sortedData;
}
ps. You may have one more div than you actually need
could you please tell me how to hide the component in reactJS and show another component?I have one button and text (hello).on button click, I want to hide button as well as text and show another text bye
here is my code
https://codesandbox.io/s/50lj63xvk
showBankDetail = () => {
console.log("====");
this.setState({
validForm: true
});
};
render() {
const validForm = !this.state.validForm;
return { validForm } ? (
<div>
heloo<button onClick={this.showBankDetail}>hide</button>
</div>
) : (
<div>bye</div>
);
}
One way is to put it on a separate variable first
showBankDetail = () => {
console.log("====");
this.setState({
validForm: true
});
};
render() {
const validForm = !this.state.validForm;
let form;
if (validForm) {
form = (<div>
heloo<button onClick={this.showBankDetail}>hide</button>
</div>);
} else {
form = (<div>bye</div>);
}
return ({form});
}
{ validForm } is creating an object with property validForm and value of validForm (e.g. true or false). You can read more about it here. Your code should look like this
showBankDetail = () => {
console.log("====");
this.setState({
validForm: true
});
};
render() {
const validForm = !this.state.validForm;
return validForm ? (
<div>
heloo<button onClick={this.showBankDetail}>hide</button>
</div>
) : (
<div>bye</div>
);
}
There are a few things you should look at. First off you want to toggle the validForm state, so do that in the showBankDetail function. You could return different elements based on validForm, but you can also do it inline. See:
class App extends React.Component {
constructor() {
super();
this.state = {
validForm: false
};
}
showBankDetail = () => {
this.setState({
validForm: !this.state.validForm
});
};
render() {
return (
<div>
{ this.state.validForm ?
<div>heloo</div> :
<div>bye</div>
}
<button onClick={this.showBankDetail}>hide</button>
</div>
)
}
}
I create a component like so:
let bList = bObj.map((obj, index) => {
let {
icon, htmlType, onClick } = obj;
let _b = <Button
htmlType = { htmlType }
icon = { icon }
onClick = { () => {this._onClick()} } />
if(someVar) {
return (
<AnotherComp style = { someVar }
key = { index } >
{ _b }
</AnotherComp>
);
} else {
return (
{ _b }
);
}
});
bList =
<div style = { wrap }>
<myComp style = { grp }>
{ buttonsList }
</myComp>
</div>
return bList;
That returns me
Uncaught Error: Objects are not valid as a React child (found: object with keys {_bu}). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons. Check the render method of MyComp.
However, when I write it like this:
let bList = bObj.map((obj, index) => {
let {
icon, htmlType, onClick } = obj;
if(someVar) {
return (
<AnotherComp style = { someVar }
key = { index } >
<Button
htmlType = { htmlType }
icon = { icon }
onClick = { () => {this._onClick()} } />
</AnotherComp>
);
} else {
return (
<Button
htmlType = { htmlType }
icon = { icon }
onClick = { () => {this._onClick()} } />
);
}
});
bList =
<div style = { wrap }>
<MyComp style = { grp }>
{ buttonsList }
</MyComp>
</div>
return bList;
It works. Where is the difference between saving <Button ../> in a variable and writing it in there directly?!
Difference is you are using extra {}, remove that it will work:
return _b;
Meaning of return ({ _b }) is:
return ({'_b' : _b});
That means you are returning an object with key _b, not the JSX.
Check this snippet:
let a = 5;
let b = { a };
console.log('b = ', b);