I am trying to use the Dropdown element of Semantic UI React. It is meant to work with a REST API that allows to get a list of movies. React is configured to fetch data from the appropriate REST API application (this already works for other elements of the frontend).
I would like to get the list of movie names as options. Please have a look at the following JS snippet.
import React, { useState, useEffect } from "react";
import { Dropdown } from "semantic-ui-react";
export const MovieDropdown = () => {
const [movie, setMovie] = useState("");
const [movieOptions, setMovieOptions] = useState([]);
useEffect(() => {
fetch("/movies")
.then((response) => response.json())
.then((data) =>
setMovieOptions(
data.map((x) => {
return { key: x.name, text: x.name, value: x.name };
})
)
);
}, []);
return (
<Dropdown
placeholder="Select Movie"
search
selection
options={movieOptions}
onChange={(e) => setMovie(e.target.value)}
/>
);
};
export default MovieDropdown;
I could not figure it out from https://react.semantic-ui.com/modules/dropdown/#usage-remote.
Your code looks good. Change a small thing and it will work:
onChange={e => setMovie(e.target.value)} // you cannot use event in setState. furthermore checkout the second param of the onChange-Event
to
onChange={(e, {value}) => setMovie(value)}
checkout fixing-react-warning-synthetic-events-in-setstate
here's the full working code
import React, { useState, useEffect } from "react";
import { Dropdown } from "semantic-ui-react";
export const MovieDropdown = () => {
const [movie, setMovie] = useState("");
const [movieOptions, setMovieOptions] = useState([]);
useEffect(() => {
fetch("/movies")
.then((response) => response.json())
.then((data) =>
setMovieOptions(
data.map((x) => {
return { key: x.name, text: x.name, value: x.name };
})
)
);
}, []);
return (
<Dropdown
placeholder="Select Movie"
search
selection
options={movieOptions}
onChange={(e, {value}) => setMovie(value)}
/>
);
};
export default MovieDropdown;
Related
hello i'm trying to delete my rows through states and axios request but i'm not sure how can i manage my api data through states, i have to update my table when i delete the row, right now if i log states dont have data to manage
import React, {useMemo} from 'react';
import {useSortBy, useTable, usePagination} from 'react-table';
import apiData from '../../../modules/searchCards/apiHooks/apiData.js';
import DeleteModal from '../../../DeleteModal/index.jsx';
import axios from 'axios';
/* eslint-disable react/prop-types */
const useTableResource = () => {
const {data} = apiData();
const resourcesData = data;
const resourcesDataLength = resourcesData.length;
// i try to convert my data into useState but is not working i'm not sure why
const [datas, setData] = React.useState(React.useMemo(() => data, []));
const resourcesColumns = useMemo(() => [
{
id: 'delete',
accessor: () => 'delete',
disableSortBy: true,
Cell: ({row}) => <div onClick={(event) => event.stopPropagation()} style={{
display: 'flex',
justifyContent: 'center'
}}>
<DeleteModal delb={async (index) => {
// here should update my data state when i delete
setData(datas.filter((item, i) => i !== index));
await axios.delete(`api/resources?_id=${row.original._id}`);
}}/>
</div>
}
], []);
const tableInstance = useTable({
columns: resourcesColumns,
data: resourcesData,
disableSortRemove: true,
}, useSortBy, usePagination);
return {
tableInstance,
resourcesColumns,
resourcesDataLength
};
};
export default useTableResource;
There's a lot going on in the code that's probably not relevant to the question, sounds like the main issue is nothing is being returned once you've deleted a record/item.
Try something like this:
const [data, setData] = useState({});
function getData() {
axios.get("https://api.example.com")
.then((response) => response.json())
.then((data) => setData(data));
}
function deleteItem(id) {
axios.delete("https://api.example.com/" + id)
.then(() => getData());
}
In this way, after the item has been deleted, you're then calling the api to get the latest update.
In another scenario, it's possible your delete api call is returning the new updated records already, in which case:
function deleteItem(id) {
axios.delete("https://api.example.com/" + id)
.then((response) => response.json())
.then((data) => setData(data));
}
Again using example code, but you should be able to go from there.
REACT.js:
Let say I have a home page with a search bar, and the search bar is a separate component file i'm calling.
The search bar file contains the useState, set to whatever the user selects. How do I pull that state from the search bar and give it to the original home page that
SearchBar is called in?
The SearchBar Code might look something like this..
import React, { useEffect, useState } from 'react'
import {DropdownButton, Dropdown} from 'react-bootstrap';
import axios from 'axios';
const StateSearch = () =>{
const [states, setStates] = useState([])
const [ stateChoice, setStateChoice] = useState("")
useEffect (()=>{
getStates();
},[])
const getStates = async () => {
let response = await axios.get('/states')
setStates(response.data)
}
const populateDropdown = () => {
return states.map((s)=>{
return (
<Dropdown.Item as="button" value={s.name}>{s.name}</Dropdown.Item>
)
})
}
const handleSubmit = (value) => {
setStateChoice(value);
}
return (
<div>
<DropdownButton
onClick={(e) => handleSubmit(e.target.value)}
id="state-dropdown-menu"
title="States"
>
{populateDropdown()}
</DropdownButton>
</div>
)
}
export default StateSearch;
and the home page looks like this
import React, { useContext, useState } from 'react'
import RenderJson from '../components/RenderJson';
import StateSearch from '../components/StateSearch';
import { AuthContext } from '../providers/AuthProvider';
const Home = () => {
const [stateChoice, setStateChoice] = useState('')
const auth = useContext(AuthContext)
console.log(stateChoice)
return(
<div>
<h1>Welcome!</h1>
<h2> Hey there! Glad to see you. Please login to save a route to your prefered locations, or use the finder below to search for your State</h2>
<StateSearch stateChoice={stateChoice} />
</div>
)
};
export default Home;
As you can see, these are two separate files, how do i send the selection the user makes on the search bar as props to the original home page? (or send the state, either one)
You just need to pass one callback into your child.
Homepage
<StateSearch stateChoice={stateChoice} sendSearchResult={value => {
// Your Selected value
}} />
Search bar
const StateSearch = ({ sendSearchResult }) => {
..... // Remaining Code
const handleSubmit = (value) => {
setStateChoice(value);
sendSearchResult(value);
}
You can lift the state up with function you pass via props.
const Home = () => {
const getChoice = (choice) => {
console.log(choice);
}
return <StateSearch stateChoice={stateChoice} giveChoice={getChoice} />
}
const StateSearch = (props) => {
const handleSubmit = (value) => {
props.giveChoice(value);
}
// Remaining code ...
}
Actually there is no need to have stateChoice state in StateSearch component if you are just sending the value up.
Hello and welcome to StackOverflow. I'd recommend using the below structure for an autocomplete search bar. There should be a stateless autocomplete UI component. It should be wrapped into a container that handles the search logic. And finally, pass the value to its parent when the user selects one.
// import { useState, useEffect } from 'react' --> with babel import
const { useState, useEffect } = React // --> with inline script tag
// Autocomplete.jsx
const Autocomplete = ({ onSearch, searchValue, onSelect, suggestionList }) => {
return (
<div>
<input
placeholder="Search!"
value={searchValue}
onChange={({target: { value }}) => onSearch(value)}
/>
<select
value="DEFAULT"
disabled={!suggestionList.length}
onChange={({target: {value}}) => onSelect(value)}
>
<option value="DEFAULT" disabled>Select!</option>
{suggestionList.map(({ id, value }) => (
<option key={id} value={value}>{value}</option>
))}
</select>
</div>
)
}
// SearchBarContainer.jsx
const SearchBarContainer = ({ onSelect }) => {
const [searchValue, setSearchValue] = useState('')
const [suggestionList, setSuggestionList] = useState([])
useEffect(() => {
if (searchValue) {
// some async logic that fetches suggestions based on the search value
setSuggestionList([
{ id: 1, value: `${searchValue} foo` },
{ id: 2, value: `${searchValue} bar` },
])
}
}, [searchValue, setSuggestionList])
return (
<Autocomplete
onSearch={setSearchValue}
searchValue={searchValue}
onSelect={onSelect}
suggestionList={suggestionList}
/>
)
}
// Home.jsx
const Home = ({ children }) => {
const [result, setResult] = useState('')
return (
<div>
<SearchBarContainer onSelect={setResult} />
result: {result}
</div>
)
}
ReactDOM.render(<Home />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.9.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Just pass a setState to component
parent component:
const [state, setState] = useState({
selectedItem: ''
})
<StateSearch state={state} setState={setState} />
change parent state from child component:
const StateSearch = ({ state, setState }) => {
const handleStateChange = (args) => setState({…state, selectedItem:args})
return (...
<button onClick={() => handleStateChange("myItem")}/>
...)
}
having an issue, when the when nav to the comp the items state is empty, if I edit the code and page refreshes its shows up and if I add the state to the useEffect "[itemCollectionRef, items]" it's an inf loop but the data is their anyone have a better idea or way to fetch the data for display from firestore.
import React, { useState, useEffect } from "react";
import { Grid, Box, Button, Space } from "#mantine/core";
import { ItemBadge } from "../../components/NFAItemBadge";
import { useNavigate } from "react-router-dom";
import { db, auth } from "../../firebase";
import { getFirestore, query, getDocs, collection, where, addDoc } from "firebase/firestore";
import { useAuthState } from "react-firebase-hooks/auth";
const ItemTrack = () => {
const [user, loading, error] = useAuthState(auth);
const navigate = useNavigate();
const [items, setItems] = useState([]);
const itemCollectionRef = collection(db, "items");
useEffect(() => {
//if(!user) return navigate('/');
//if(loading) return;
const q = query(itemCollectionRef, where("uid", "==", user.uid));
const getItems = async () => {
const data = await getDocs(q);
setItems(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
console.log("Fetched Items: ", items);
};
getItems();
}, []);
if (loading) {
return (
<div>
<p>Initialising User....</p>
</div>
);
}
if (error) {
return (
<div>
<p>Error: {error}</p>
</div>
);
}
if (user) {
return (
<Box sx={{ maxWidth: 1000 }} mx="auto">
</Box>
);
} else {
return navigate("/");
}
};
export default ItemTrack;
It will depend how you will render the data from the useEffect. setState does not make changes directly to the state object. It just creates queues for React core to update the state object of a React component. If you add the state to the useEffect, it compares the two objects, and since they have a different reference, it once again fetches the items and sets the new items object to the state. The state updates then triggers a re-render in the component. And on, and on, and on...
As I stated above, it will depend on how you want to show your data. If you just want to log your data into your console then you must use a temporary variable rather than using setState:
useEffect(() => {
const newItems = data.docs.map((doc) => ({ ...doc.data(), id: doc.id }))
console.log(newItems)
// setItems(newItems)
}, [])
You could also use multiple useEffect to get the updated state object:
useEffect(() => {
setItems(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })))
}, [])
useEffect(() => { console.log(items) }, [items])
If you now want to render it to the component then you have to call the state in the component and map the data into it. Take a look at the sample code below:
useEffect(() => {
const q = query(itemCollectionRef, where("uid", "==", user.uid));
const getItems = async () => {
const data = await getDocs(q);
setItems(data.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
};
getItems();
}, []);
return (
<div>
<p>SomeData: <p/>
{items.map((item) => (
<p key={item.id}>{item.fieldname}</p>
))}
</div>
);
I'm trying to render multiple cards by pulling data from the API. But the return is an array, I don't understand why the map is not working.
const CharacterCard = () => {
const [showModal, setShowModal] = useState(false)
const openModal = () => {
setShowModal(prev => !prev)
}
const characters = useRequestData([], `${BASE_URL}/characters`)
const renderCard = characters.map((character) => {
return (
<CardContainer key={character._id} imageUrl={character.imageUrl}/>
)
})
return (
<Container>
{renderCard}
<ModalScreen showModal={showModal} setShowModal={setShowModal} />
</Container>
)
}
export default CharacterCard
The hook is this
import { useEffect, useState } from "react"
import axios from "axios"
const useRequestData = (initialState, url) => {
const [data, setData] = useState(initialState)
useEffect(() => {
axios.get(url)
.then((res) => {
setData(res.data)
})
.catch((err) => {
console.log(err.data)
})
}, [url])
return (data)
}
export default useRequestData
console error image
requisition return image
API: https://disneyapi.dev/docs
Looks like the default value of the characters is undefined.
So something like (characters || []).map.. will help I think.
For deeper look at this you can debug useRequestData hook, as I can't see the source of that hook from you example
I am using Semantic UI React.
The following JS code does not work for me:
import React, { useState, useEffect } from "react";
import { Dropdown, Form, Button } from "semantic-ui-react";
export const MovieDropdown = () => {
const [movie, setMovie] = useState("");
const [person, setPerson] = useState("");
const [movieOptions, setMovieOptions] = useState([]);
const [personOptions, setPersonOptions] = useState([]);
useEffect(() => {
Promise.all([
fetch("/people").then(res =>
res.json()
),
fetch("/movies").then(res =>
res.json()
)
])
.then(([res1, res2]) => {
console.log(res1, res2);
var make_dd = (rec) => {
rec.map(x => {
return {'key': x.name, 'text': x.name, 'value': x.name}
})
}
setPersonOptions(make_dd(res1))
setMovieOptions(make_dd(res2))
})
.catch(err => {
console.log(err);
});
});
return (
<Form>
<Form.Field>
<Dropdown
placeholder="Select Movie"
search
selection
options={movieOptions}
onChange={(e, {value}) => setMovie(value)}
/>
</Form.Field>
<Form.Field>
<Dropdown
placeholder="Select Person"
search
selection
options={personOptions}
onChange={(e, {value}) => setPerson(value)}
/>
</Form.Field>
</Form>
);
};
export default MovieDropdown;
Problem is that I lose the DB connection when running this component. I tried with MySQL and SQLite and it gives the same issue.
How to solve this? Should I have 1 fetch per component?
I thank you in advance.
Kind regards,
Theo
Well, I dont know about the DB Connetion, but the remmended way of calling api in useEffect is like this:
useEffect({
// your code here only once
},[])
OR,
useEffect({
// your code here will run whenever id changes
},[id])
Your useEffect will run on every render,which is not recommended time/ way to make api calls.