I'm trying to display list of events based on the search query dynamically.
The problem is that I'm always on the initial View and the renderSearch View is never called.
PastEvent is a function called from the primary redner of the class by scenemap
Please check comments in the code.
//to display the past events tab
PastEvents = () => {
const state = this.state;
let myTableData = [];
if (
state.PastEventList.length !== 0
) {
state.PastEventList.map((rowData) =>
myTableData.push([
this.renderRow(rowData)
])
);
}
function renderPast() {
console.log("im in render past") //shows
return (
<ScrollView horizontal={false}>
<Table style={styles.table}>
{myTableData.map((rowData, index) => (
<Row
key={index}
data={rowData}
style={styles.row}
textStyle={styles.rowText}
widthArr={state.widthArr}
/>
))}
</Table>
</ScrollView>
)
}
function renderSearch() {
console.log("im in render search") //never shows even after changing the text
let searchTable = [];
if (
this.state.seacrhPastList.length !== 0
) {
state.seacrhPastList.map((rowData) =>
searchTable.push([
this.renderRow(rowData)
])
);
}
return (
<ScrollView horizontal={false}>
<Table style={styles.table}>
{searchTable.map((rowData, index) => (
<Row
key={index}
data={rowData}
style={styles.row}
textStyle={styles.rowText}
widthArr={state.widthArr}
/>
))}
</Table>
</ScrollView>
)
}
return (
<View style={styles.container}>
<TextInput placeholder="Search for Events" onChangeText={text => this.onChangeSearch(text)}></TextInput>
{this.state.searching ? renderSearch() : renderPast()} //please check the onchangeSearch function
</View>
)
}
And the function of change is like that:
onChangeSearch = (text) => {
if (text.length > 0) {
let jsonData = {};
//get list of events
let url = "/api/FindEvents/" + text.toLowerCase()
ApiHelper.createApiRequest(url, jsonData, true).then(res => {
if (res.status == 200) {
this.state.seacrhPastList = res.data
this.state.searching= true //I was hoping this change will cause the render
}
})
.catch(err => {
console.log(err);
return err;
});
}
}
How can i change the events based on the query of the input ? Thank you
you need to use useState here
declare useState like this:
PastEvents = () => {
const [searching, setText] = useState(false);
change the searching state here:
if (res.status == 200) {
this.state.seacrhPastList = res.data
setText(true);
}
Hope this helps!
You're in a stateless component you shouldn't use "this" in any way, also you can't use state that way, you need to use react hooks
Import { useState } from 'react'
Then you can use state in a functional component
const [state, setState] = useState(initialvalue);
Related
I retrieve a list of jobs using useQuery(), each one have a Favourite icon (filled depending if it's favourited)
If I click that button, I managed to refresh the item Favourite icon, but it refreshes all the items.
Whats the correct way to avoid that? Because it appears the Loading wheel again, and I think it has to be a more elegant way.
const Openings = () => {
const onToggleFav = () => {
setFavCount(prev => prev + 1)
}
const [favCount, setFavCount] = useState(0);
const { isLoading, data } = useQuery(
['getRecruiterOpenings', favCount],
() => getRecruiterOpenings()
);
return (
<div>
{ isLoading ? <Loading /> : (
<>
{ data && data.openings && data.openings.map((opening) => (
<>
<Opening {...opening} onToggleFav={() => onToggleFav()} key={opening.id}/>
</>
))}
</>
)}
</div>
)
}
export default Openings;
Inside Opening component I have a method that dispatches when you click the fav icon:
const toggleFav = async (e) => {
e.preventDefault();
await toggleFavOpening(fav, id).then(() => {
if(onToggleFav) onToggleFav()
});
}
Damn, two days, two noob questions, sorry guys.
Yesterday, I spent the whole afternoon reading the docs but my fart-ey brain cannot process how to use react hooks to pass data from a child to a parent.
I want to create a button on my parent that can listen to his child's state to check on it and change the background color depending on its value.
Thing is, the child component is mapping some stuff so I cannot create a button (otherwhise it would be rendered multiple times and not only once like I want).
I've thought about moving all the data to my parent component but I cannot understand how since I'm fairly new to React and it's been only two months of learning how to code for me basically.
I will now provide the code for the parent and the child component.
The parent :
import React from "react";
import Quizz from "./components/Quizz";
export default function App() {
const [quizz, setQuizz] = React.useState([]);
React.useEffect(() => {
async function getData() {
const res = await fetch(
"https://opentdb.com/api.php?amount=5&category=27&type=multiple"
);
const data = await res.json();
setQuizz(data.results)
}
getData();
}, []);
function checkOnChild(){ /* <== the function I'd like to use to check on my Quizz component's "activeAnswer" state */
console.log(quizz);
}
const cards = quizz.map((item, key) => {
return <Quizz {...item} key={key}/>;
});
return (
<div>
{cards}
<button onClick={checkOnChild}>Check answers</button> /* <== the button that will use the function */
</div>
);
}
and the child :
import React from "react";
import { useRef } from "react";
export default function Quizz(props) {
const [activeAnswer, setActiveAnswer] = React.useState('');/* <== the state I'd like to check on from my parent component */
function toggle(answer) {
setActiveAnswer(answer);
}
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
let temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
let answers = props.incorrect_answers;
const ref = useRef(false);
if (!ref.current) {
answers.push(props.correct_answer);
shuffleArray(answers);
ref.current = true;
}
const answerDiv = answers.map((answer, key) => (
<div key={key} className="individuals" onClick={()=> toggle(answer)}
style={{background: answer == activeAnswer ? "#D6DBF5" : "transparent" }}>
{answer}
</div>
));
console.log(answers);
console.log(activeAnswer);
console.log(props.correct_answer);
return (
<div className="questions">
<div>
<h2>{props.question}</h2>
</div>
<div className="individuals__container">{answerDiv}</div>
<hr />
</div>
);
}
I'm really sorry If it seems dumb or if I'm making forbidden things lmao, but thanks in advance for your help guys!
This should get you a bit further I think.
export default function App() {
const [quizData, setQuizData] = useState([])
const [quizState, setQuizState] = useState({})
useEffect(() => {
async function getData() {
const res = await fetch('https://opentdb.com/api.php?amount=5&category=27&type=multiple')
const data = await res.json()
const results = data.results
setQuizData(results)
setQuizState(results.reduce((acc, curr) => ({ ...acc, [curr.question]: '' }), {}))
}
getData()
}, [])
function checkOnChild() {
console.log(quizState)
}
const cards = quizData.map((item) => {
return <Quizz {...item} key={item.question} quizState={quizState} setQuizState={setQuizState} />
})
return (
<div>
{cards}
<button onClick={checkOnChild}>Check answers</button>
</div>
)
}
export default function Quizz(props) {
function handleOnClick(answer) {
props.setQuizState(prevState => ({
...prevState,
[props.question]: answer,
}))
}
const answers = useMemo(() => {
const arr = [...props.incorrect_answers, props.correct_answer]
return shuffleArray(arr)
}, [props.incorrect_answers, props.correct_answer])
const answerDiv = answers.map((answer) => (
<div
className="individuals"
key={answer}
onClick={() => handleOnClick(answer)}
style={{ background: answer == props.quizState[props.question] ? '#D6DBF5' : 'transparent' }}
>
{answer}
</div>
))
return (
<div className="questions">
<div>
<h2>{props.question}</h2>
</div>
<div className="individuals__container">{answerDiv}</div>
<hr />
</div>
)
}
I am creating a custom multiple choice question, but I am having difficulties updating my selection choice using useState.
const QuestionPage = ({ audioFiles }) => {
const [choice, setChoice] = useState(-1); // -1 is when none of the choices are selected
const updateChoice = val => {
setChoice(val);
}
return (
// each MultipleChoice contains an audio file and a radio button
<MultipleChoice audioFiles={audioFiles} choice={choice} updateChoice={updateChoice} />
)
};
const MultipleChoice = ({ audioFiles, choice, updateChoice }) => {
const answerOption = audioFiles.map((item, key) =>
<AudioButton file={file} index={key} choice={choice} updateChoice={updateChoice} />
);
return (
{answerOption}
);
}
const AudioButton = ({ file, index, choice, updateChoice }) => {
const handleClick = (val) => {
updateChoice(val);
};
const radioButton = (
<div className={`${index === choice ? "selected" : ""}`} onClick={() => handleClick(index)}>
</div>
);
return (
<>
{radioButton}
<Audio file={file} />
</>
);
}
In the first function, QuestionPage within updateChoice, when I use console.log(val), it updates according to the selections I make (i.e. 0 and 1). However, when I call console.log(choice), it keeps printing -1.
In addition, I keep getting an error message that says updateChoice is not a function.
Any advice? Thanks in advance!
Looks like you did not pass the value of audioFiles in MultipleChoice function
I got an error :
index.js:1 Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
And I have been trying to find what makes that error and I found the thing that makes an error.
so I tried to search how to avoid this error in this case. but I couldn't find it.
so The problem is when I upload the csv file and then the file contains info state.
so I show this file information on my website.
And when the file is uploaded then the component is changing
So I used it with the ternary operator. So I tried to remove the ternary operator then the error had disappeared I assumed that it made the error .
So I'm trying to fix it but I can't figure it out
here is my code :
const CsvShowData = ({ info, setInfo }) => {
return (
//
<>
{info.length !== 0 ? (
<DataTable>
{info.slice(0, 1).map(inf => (
<MainRow key={inf}>
{inf.map((d, index) => (
<Row key={index}>
<div className="titleRow">
<h3>{d}</h3>
</div>
</Row>
))}
</MainRow>
))}
{info.slice(1, 10).map((a, key) => (
<MainRow key={key}>
{a.map((b, idx) => (
<Row key={idx}>
<div className="sideRow">
<p>{b}</p>
</div>
</Row>
))}
</MainRow>
))}
</DataTable>
) : (
<CsvTable>
<CsvFileReader info={info} setInfo={setInfo} />
</CsvTable>
)}
</>
);
};
Thank you in advance!
CsvFileReader Component
const CsvFileReader = ({ setInfo }) => {
const handleOnDrop = data => {
const infos = data.map(item => item.data);
setTimeout(() => setInfo([...infos]), 1000); // save timeout ref
};
const handleOnError = (err, file, inputElem, reason) => {
console.log(err);
};
const handleOnRemoveFile = data => {
console.log(data);
};
return (
<>
<MainReader>
<CSVReader
onDrop={handleOnDrop}
onError={handleOnError}
config={
(({ fastMode: true }, { chunk: "LocalChunkSize" }),
{ header: false })
}
addRemoveButton
onRemoveFile={handleOnRemoveFile}
>
You should use a ref to save setTimeout and remove setInfo when component is unmounted.
const ref = useRef();
const handleOnDrop = (data) => {
const infos = data.map((item) => item.data);
ref.current = setTimeout(() => setInfo([...infos]), 1000); // save timeout ref
};
useEffect(() => {
return () => {
if (ref.current) {
clearTimeout(ref.current);
}
};
});
Can't manage to make useRef/createRef to get any other div's other then what was added last. How can i make it so when the button is clicked the ref to the div changes.
I've tried with both useRef and createRef. Since I want to make a new instance of ref, i've looked more into createRef rather then useRef.
I've also played around useEffect. But my solution didn't help me with my biggest problem
I have made a small project containing 3 components to help you understand what I'm trying to explain.
I also have a database containing mock data -> in my real project this isn't the problem. It's an array containing objects.
[{'id':'1', 'name':'first'},...]
Main:
const MainComponent = () => {
const dataRef = React.createRef(null)
React.useEffect (() => {
if(dataRef && dataRef.current){
dataRef.current.scrollIntoView({ behavior:'smooth', block:'start' })
}
},[dataRef])
const _onClick = (e) => {
dataRef.current.focus();
}
return(
<>
{data && data.map((entry, index) =>{
return <ButtonList
key={index}
entry={entry}
onClick={_onClick}
/>
})}
{data && data.map((entry, index) =>{
return <ListingAllData
key={index}
dataRef={dataRef}
entry={entry}
index={index}/>
})}
</>
)
}
Button Component
const ButtonList = ({ entry, onClick }) => {
return <button onClick={onClick}>{entry.name}</button>
}
Listing data component
const ListingAllData = (props) => {
const {entry, dataRef } = props;
return (
<div ref={dataRef}>
<p>{entry.id}</p>
<p>{entry.name}</p>
</div>
);
}
I've console logged the data.current, it only fetches the last element. I hoped it would fetch the one for the button I clicked on.
I think the main idea here is to create dynamic refs for each element (array of refs), that's why only the last one is selected when app renders out.
const MainComponent = () => {
const dataRefs = [];
data.forEach(_ => {
dataRefs.push(React.createRef(null));
});
const _onClick = (e, index) => {
dataRefs[index].current.focus();
dataRefs[index].current.scrollIntoView({
behavior: "smooth",
block: "start"
});
};
return (
<>
{data &&
data.map((entry, index) => {
return (
<ButtonList
key={index}
entry={entry}
onClick={e => _onClick(e, index)}
/>
);
})}
{data &&
data.map((entry, index) => {
return (
<>
<ListingAllData
key={index}
dataRef={dataRefs[index]}
entry={entry}
index={index}
/>
</>
);
})}
</>
);
};
Created working example in code sandbox.
https://codesandbox.io/s/dynamic-refs-so25v
Thanks to Janiis for the answer, my solution was:
in MainComponent
...
const refs = data.reduce((acc, value) => {
acc[value.id] = React.createRef();
return entry;
}, {});
const _onClick = id => {
refs[id].current.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
....
then i passed it through to the child and referred like
<div ref={refs[entry.id]}>