I code community I have a bug in my search bar. The app is very easy... Ever time a user creates a name it displays in a list array. My problem is that when the user creates a name that name is not displaying my the list but when I search the name, it does show the list.
Here the video so you can have a better understanding
https://www.youtube.com/watch?v=WIM-H4xXqMw
and the code. I need help in my search bar and display the list when clicking the button
FolderHome.js - component
const [folder, emptyFolder] = useState([]);
const data = folder;
const [searchTerm, setSearchTerm] = useState("");
let [filteredData, setFilteredData] = useState(data);
//this function is when the user press the button we want to use the folderName(value) and display in a list.
//then when we add a foldername we update and add a new folder name.
//To understand I am just adding a folder name into my empty array. Now my array as a Folder inside.
const addFolder = (folderName) => {
emptyFolder((currentFolder) => [
...currentFolder,
{ id: Math.random().toString(), value: folderName },
]);
};
//Search
const _searchFilterFunction = (event, data) => {
let newData = [];
setSearchTerm({ searchTerm: event });
if (event) {
newData = data.filter((item) => {
const textData = event.toUpperCase();
const nameFolder = item.value.toUpperCase();
return nameFolder.includes(textData);
});
setFilteredData([...newData]);
} else {
setFilteredData([...data]);
}
};
return (
<View style={styles.HomeContainer}>
<TextInput
underlineColorAndroid="transparent"
autoCapitalize="none"
placeholderTextColor="#9a73ef"
style={styles.search}
placeholder="Search"
onChangeText={(value) => {
_searchFilterFunction(value, data);
}}
/>
<FolderInput myFolder={addFolder} />
{filteredData.map((item, index) => {
return <Text key={item.id}>{item.value}</Text>;
})}
</View>
);
};
FolderInput.js - Component
const FolderInput = (props) => {
//This function we handle the input and output off Folder
const [outputFolder, inputFolder] = useState("");
//This arrow function is handling the folder name on onChangeText in TextInput
const folderName = (entereName) => {
inputFolder(entereName);
};
//Function to clear input when done enter folder name
const clearInput = () => {
props.myFolder(outputFolder);
inputFolder("");
};
return (
// TouchableWithoutFeedback allow to register a touche withou nothing happen to it
<View style={styles.outputContainer}>
<TouchableWithoutFeedback
onPress={() => {
Keyboard.dismiss();
}}
>
<View style={styles.containerFolder}>
<TextInput
blurOnSubmit
placeholder="Create Folder"
style={styles.containerTextInput}
onChangeText={folderName}
//we pass the name folder into is value by biding and store into outputFolder
value={outputFolder}
/>
<TouchableOpacity>
<AntDesign
onPress={clearInput}
name="addfolder"
backgroundColor="black"
size={30}
/>
</TouchableOpacity>
</View>
</TouchableWithoutFeedback>
</View>
);
};
export default FolderInput;
Thank you so much for all hard work your in advance
:)
You should not have filteredData as state. it is a computed result of searchTerm and folder.
just declare it as:
const filteredDate = folder.filter((item) => {
const textData = searchTerm.toUpperCase();
const nameFolder = item.value.toUpperCase();
return nameFolder.includes(textData);
});
You can take a look at a simple implementation of search in this snack:
https://codesandbox.io/s/cocky-cori-n704s?file=/src/App.js
Related
i'm retrieving a document collection as this
const retrieveCollection = async () => {
let questionDocs: Questions[] = [];
const questionCol = collection(db, "questionsdb");
const colQuery = query(questionCol);
const querySnapshot = await getDocs(colQuery);
querySnapshot.forEach((doc: DocumentData) => questionDocs.push(doc.data()));
setQuestions(questionDocs);
};
useEffect(() => {
retrieveCollection();
}, []);
Each question has an author, when i click a button i want it to show just my questions, if i click it again, it will show all the other questions. This is my function to do it
const [myQuestions, setMyQuestions] = useState(false);
useEffect(() => {
const showMyQuestions = async () => {
await retrieveCollection();
console.log(questions);
const myQuestionFilter = questions?.filter(
(item) => item.author.uid === user?.uid
);
setQuestions(myQuestionFilter!);
};
const showAllQuestions = async () => {
await retrieveCollection();
const myQuestionFilter = questions?.filter(
(item) => item.author.uid !== user?.uid
);
setQuestions(myQuestionFilter!);
};
myQuestions ? showMyQuestions() : showAllQuestions();
}, [myQuestions]);
And this is the button
<TouchableOpacity onPress={() => setMyQuestions(!myQuestions)}>
<Text>
{myQuestions ? "My Questions" : "All Questions"}
</Text>
</TouchableOpacity>
I only have questions asked by myself, so when MyQuestions is true i should see all questions, and when it's false i should see nothing. But once everything disappears it doesn't comeback. Before i filter the questions array, i call the function retrieveCollection to get all questions and then filter it.
You only fetch all your data when the component mounts and store it in questions. Its value is further manipulated by myQuestions: when true filters questions for items you've posted, and when false filter for ones not posted by you. If you toggled it twice, then you would delete everything in questions and require another api call to be made.
To avoid this, you should have another variable for myQuestions to manipulate, which is used to render the visible questions:
// update your JSX to use this variable
const [visibleQuestions, setVisibleQuestions] = useState(questions || []);
return (
{/*
.
.
.
*/}
<TouchableOpacity
onPress={() => {
const newVal = !myQuestions;
setMyQuestions(newVal);
const myQuestionFilter = questions?.filter((item) =>{
const isMyPost = item.author.uid === user?.uid
return newVal ? isMyPost : !isMyPost
);
setVisibleQuestions(myQuestionFilter)
}}
}>
<Text>
{myQuestions ? "My Questions" : "All Questions"}
</Text>
</TouchableOpacity>
)
Doing it this way you arent actually deleting the data sent from firebase and thus you wont have to retrieve it again
i am using socket.io or getting live crypto prices . it gives new prices each 10second. i have applied search functionality to get live price. but on every 10 sec my serach function get reverted and go back to normal state .
my code is below
const Item = ({ name }) => {
return (
<View style={styles.item}>
<Text>{name}</Text>
</View>
);
};
const renderItem = ({ item }) => <Item name={item.name} />;
const App = () => {
// this.arrayholder = DATA;
const [loading, setloading] = useState(false);
const [data, setdata] = useState("");
const [dasta, setdsata] = useState(DATA);
const [error, seterror] = useState(null)
const [searchValue, setsearchValue] = useState("")
useEffect(() => {
// setLoading(true);
var yui = socket.on("chat", (data) => {
setdata(data)
});
// console.log(yui.data)
}, []);
searchFunction = (text) => {
const updatedData = data.filter((item) => {
const item_data = `${item.name.toUpperCase()})`;
const text_data = text.toUpperCase();
return item_data.indexOf(text_data) > -1;
});
setdata(updatedData)
setsearchValue(text)
};
return (
<View style={styles.container}>
<SearchBar
placeholder="Search Here..."
lightTheme
round
value={searchValue}
onChangeText={(text) => searchFunction(text)}
autoCorrect={false}
/>
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={(item) => item.id}
/>
</View>
);
}
can u help pls . like when i search bitcoin it shows me bitcoin but after 10 seconds and it shows all the coin name .
you can also see here promblem
To maintain a current search state persistent although new coins are added to the list
you should call the searchFunction with the searchValue
Like
searchFunction(searchValue)
after getting the latest coin list
As previous value is saved in searchValue it will filter again your list
Form mine understanding without testing your code I assume you are getting you new data in useEffect
var yui = socket.on("chat", (data) => {
setdata(data);
});
So here call
searchFunction(searchValue)
After
setdata(data);
Or Simply replace your useEffect with
useEffect(() => {
// setLoading(true);
var yui = socket.on("chat", (data) => {
setdata(data);
searchFunction(searchValue);
});
// console.log(yui.data)
}, []);
I've a weird behavior here.
I'm trying to update a parent component from a child.
I've thus something like this for the child:
const LabelList = ({editable, boardLabels, cardLabels, size='normal', udpateCardLabelsHandler}) => {
return (
<DropDownPicker
labelStyle={{
fontWeight: "bold"
}}
badgeColors={badgeColors}
showBadgeDot={false}
items={items}
multiple={true}
open={open}
onChangeValue={(value) => udpateCardLabelsHandler(value)}
value={value}
setOpen={setOpen}
setValue={setValue} />
)
}
And, for the parent, something like this:
const CardDetails = () => {
const [updatedCardLabels, setUpdatedCardLabels] = useState([])
const [card, setCard] = useState({})
const [editMode, setEditMode] = useState(false)
// Handler to let the LabelList child update the card's labels
const udpateCardLabelsHandler = (values) => {
const boardLabels = boards.value[route.params.boardId].labels
const labels = boardLabels.filter(label => {
return values.indexOf(label.id) !== -1
})
console.log('updated labels',labels)
setUpdatedCardLabels(labels)
}
return (
<View style={{zIndex: 10000}}>
<Text h1 h1Style={theme.title}>
{i18n.t('labels')}
</Text>
<LabelList
editable = {editMode}
boardLabels = {boards.value[route.params.boardId].labels}
cardLabels = {card.labels}
udpateCardLabelsHandler = {udpateCardLabelsHandler} />
</View>
)
And, this just doesn't work: As soon as I try changing something in the DropDownPicker the application hangs. The console.log statement isn't even executed and no errors show up in my expo console.
What's strange is that if I change the updateCardLabels state to be a boolean for example, everything works ok (eg: the console.log statement is executed):
const [updatedCardLabels, setUpdatedCardLabels] = useState(false)
// Handler to let the LabelList child update the card's labels
const udpateCardLabelsHandler = (values) => {
const boardLabels = boards.value[route.params.boardId].labels
const labels = boardLabels.filter(label => {
return values.indexOf(label.id) !== -1
})
console.log('updated labels',labels)
setUpdatedCardLabels(true)
}
Please note that updatedCardLabels isn't used anywhere: it's a dummy variable that I'm just using to debug this issue (to make sure I was not ending in some endless render loop or something similar).
For the sake of completeness, here's what labels looks like at line console.log('updated labels',labels) (please not that I can only see this value when doing setUpdatedCardLabels(true) as otherwise, when the code does setUpdatedCardLabels(labels), the console.log statement is not executed, as mentioned earlier):
updated labels Array [
Object {
"ETag": "a95b2566521a73c5edfb7b8f215948bf",
"boardId": 1,
"cardId": null,
"color": "CC317C",
"id": 9,
"lastModified": 1621108392,
"title": "test-label",
},
]
Does anybody have an explanation for this strange behavior?
Best regards,
Cyrille
So, I've found the problem: It was a side effect of the DrowpDownPicker.
I've solved it by changing my child as follow:
const LabelList = ({editable, boardLabels, cardLabels, size='normal', udpateCardLabelsHandler}) => {
const [open, setOpen] = useState(false);
const [value, setValue] = useState(cardLabels.map(item => item.id));
const theme = useSelector(state => state.theme)
// Updates parent when value changes
useEffect(() => {
if (typeof udpateCardLabelsHandler !== 'undefined') {
udpateCardLabelsHandler(value)
}
}, [value])
return (
<DropDownPicker
labelStyle={{
fontWeight: "bold"
}}
badgeColors={badgeColors}
showBadgeDot={false}
items={items}
multiple={true}
open={open}
value={value}
setOpen={setOpen}
setValue={setValue} />
)
Agenda doesn't update even when new data are added. Only when the app is reloaded it updates.
Here is my code:
const CalendarScreen = () => {
const list = useSelector((state) => state.getTodo.list);
const [items, setItems] = useState({});
const loadItems = () => {
list.forEach((data) => {
const strTime = data.createdDate;
if (!items[strTime]) {
items[strTime] = [];
list.forEach((datalist) => {
items[strTime].push({
name: datalist.title,
});
});
}
});
const newItems = {};
Object.keys(items).forEach((key) => {
newItems[key] = items[key];
});
setItems(newItems);
};
const renderItem = (item) => {
return (
<View >
<Text>{item.name}</Text>
</View>
);
};
return (
<View style={flex: 1}>
<Agenda
items={items}
loadItemsForMonth={loadItems}
renderItem={renderItem}
pastScrollRange={1}
futureScrollRange={1}
/>
</View>
);
};
export { CalendarScreen };
Expectation: Just want the Agenda to update automatically when new data is added in the state instead of having to reload the app.
It looks like that refresh depends call of loadItemsForMonth.
Unfortunately I cannot see when Agenda call loadItemsForMonth
im new in react native, and im doing a note block, the problem now its that once i click save, it saves it to the array but when i get back to home screen, where i show the notes that are saved it doesnt show the last one, until i re load the entire project, how can I do to re render it? i have seen that i have to use this.forceUpdate(), but it doesnt working either, heres the code:
this is the home screen, the first screen the user will see, it shows the notes that are saved calling the component Notes
render() {
return (
<>
<View style = {this.styles.container}>
<View>
<Text style = {this.styles.Text}>Welcome to home!</Text>
</View>
<Notes></Notes>
<View style = {this.styles.View}>
<Button title = "Create new note" styles = {this.styles.Button} onPress = {() => this.props.navigation.navigate("Create_note")}></Button>
</View>
<View style = {this.styles.View}>
<Button title = "Notes" styles = {this.styles.Button} onPress = {() =>this.props.navigation.navigate("See_notes")}></Button>
</View>
</View>
</>
);
}
heres the component Notes:
class Notes extends Component {
constructor(props) {
super(props);
this.state = {
array_notes: [],
}
}
componentDidMount() {
this.fetch_notes();
}
fetch_notes = async() => {
try {
const data = await AsyncStorage.getItem("array_notes");
if (data != null) {
const array_notes = JSON.parse(data);
this.setState({array_notes: array_notes});
}else {
console.log("with no data");
}
}catch (error) {
console.log(error);
}
}
render() {
return (
<>
<View style = {this.styles.View}>
<FlatList data = {this.state.array_notes} renderItem = {({item}) => (<Text style = {this.styles.Text}>{item.title}</Text>)} keyExtractor = {(item) => item.title}></FlatList>
</View>
</>
);
}
and heres the create a new note screen, where the user type a new note:
class Create_note extends Component {
constructor() {
super();
this.state = {
title: "",
content: "",
}
}
save_Data = async() => {
try {
const array_notes = await AsyncStorage.getItem("array_notes");
if (array_notes === null) {
const array_notes = [];
await AsyncStorage.setItem("array_notes", JSON.stringify(array_notes));
}else {
const new_note = {'title': this.state.title, 'content': this.state.content};
const array_notes = JSON.parse(await AsyncStorage.getItem("array_notes"));
array_notes.push(new_note);
await AsyncStorage.setItem("array_notes", JSON.stringify(array_notes));
}
}catch(error) {
console.log(error);
}
}
}
render() {
return (
<>
<Text style = {this.styles.Text }>Welcome to Shum Note!</Text>
<View>
<TextInput style = {this.styles.TextInput_title} placeholder = "Title" multiline = {true} maxLength = {80} onChangeText = {(title) => this.setState({title: title})}></TextInput>
<TextInput style = {this.styles.TextInput_content} placeholder = "Content" multiline = {true} onChangeText = {(content) => this.setState({content: content})}></TextInput>
<Button title = "Save" onPress = {this.save_Data}></Button>
</View>
<View style = {this.styles.back_Button}>
<Button title = "Go back home" onPress = {() => this.props.navigation.navigate("Home")}></Button>
</View>
</>
);
}
once i saved the new note and press the go back home it doesnt show the last one until like i said, i reload the entire project, but something curious, is if i go to create_note screen it will re render each time, but it doesnt happend with home, why?
You have to pass in fetch_notes as a prop in Create_note.
this.props.navigation.navigate("Create_note", fetch_notes: this.fetch_notes)
In your Create_note get the function from navigation.
const { fetch_notes} = this.props.route.params; //https://reactnavigation.org/docs/params/
After saving the note you have to call it like this: this.props.fetch_notes()
You can add the.props.navigation.addListener. When you are back from next screen to previous screen API calling because of addListener focus on current screen and UI is render because of state changes.
componentDidMount() {
this.focusListener =
this.props.navigation.addListener("didFocus", () => {
this.fetch_notes()
});
}