I am creating a data table, fetching data from Redux store, and setting up this Redux's raw data into a useState hook named rowsData, setRowsData for data table rows,
The data from redux is array of objects.
I then set the rowsData into a new [data, setData] useState because of some additional data, like meta information for pagination.
useEffect(() => {
const rawRows =
users.allUsers &&
users.allUsers.data.map((user) => {
return {
name: (
<Link
to={`/users/profile/view/${user.secondaryId}`}
className="d-flex justify-content-start align-items-center"
>
<div className="me-3">
<AvatarWord initial={user.name[0]} />
</div>
<h5 className="text-primary mt-2"> {user.name}</h5>
</Link>
),
primaryRole: primaryRoleBackground(
user.primary_role
? user.primary_role.name[0].toUpperCase() +
user.primary_role.name.slice(1)
: ""
),
// primary_role: primaryRoleBackground(user.primary_role),
id: user.id,
email: user.email,
status: <div>{userStatus(user.status, user.id)}</div>,
Here I am adjusting it according to data table. The last line ````Status: ``` has a function to discriminate the active and inactive users with a button to activate or deactivate them.
const userStatus = (status, id) => {
switch (status) {
case "0":
return (
<>
<span className="legend-indicator bg-danger text-dark"></span>
Inactive
<Link
to="#!"
type="button"
className="btn badge bg-success ms-2"
onClick={() => userActivator(id)}
>
Activate
</Link>
</>
);
case "1":
return (
<>
<span className="legend-indicator bg-success text-dark"></span>
Active
<Link
to="#!"
type="button"
className="btn badge bg-danger ms-2"
onClick={() => userDeactivator(id)}
>
Deactivate
</Link>
</>
);
default:
return;
}
};
Here is the output of the codes above.
now when I click on deactivate I use the following code to update the data.
const userDeactivator = (id) => {
// deactivateUser(id);
console.log(rowsData.length);
for (let i = 0; i === dataLength; i++) {
const res = rowsData[i].id === id;
setRowsData([
...rowsData,
{
...res,
status: (
<div>
<>
<span className="legend-indicator bg-danger text-dark"></span>
Inactive
<Link
to="#!"
type="button"
className="btn badge bg-success ms-2"
onClick={() => userActivator(res.id)}
>
Activate
</Link>
</>
</div>
),
},
]);
}
};
I sure sends the API call to deactivate the user, but I have to update the data status in runtime. What I am missing I cannot figure out.
In useEffect hook you have to pass the argument of status like this:
useEffect(() => {
return () => {
// Your Code
}
}, [status])
This would tell the useEffect that whenever the status changes you have to re-render the state for it.
Related
I have a following structure in React, but whenever I click on the buttons, the rendering is not changed:
function App() {
const [showTxt1, setShowTxt1] = useState(false);
const [showTxt2, setShowTxt2] = useState(false);
const setShowButtons = (selected) => {
console.log("selected:", selected);
setShowTxt1(selected === "txt1");
setShowTxt2(selected === "txt2");
}
return (
<React.Fragment>
<div className="main">
<div className="main-options">
<button
type="button"
className="btn btn-primary"
onClick={() => {
setShowButtons("txt1");
}}
>
Show Txt1
</button>
<button
type="button"
className="btn btn-primary"
onClick={() => {
setShowButtons("txt2");
}}
>
Show Txt2
</button>
</div>
{
showTxt1 && <p>text1</p>
}
{showTxt2 && <p>text2</p>
}
</div>
</React.Fragment>
);
}
I see the log on the console, but the page is not updated. On the 'Components' tab in debugging, I only get an error message: 'Timed out, while inspecting...'
This is a simplified version of my app, where I think the issue is.
Sometimes when I open a new tab (and thus the debugger is closed), it works. What can cause the issue?
I am using React Context to create a multistep form, the form must keep the selection when the user clicks the next or previous step. I am using the below code and it's working fine until I reach the stage to add multiple features using the checkbox, once items are checked, user can go to the previous step to edit and press next to go to the next stage where checked checkboxes must remain checked. I cannot figure out how to push each checkbox value to the features array and remove the item from array when the user uncheck. The important part is to retain the selected despite user go to previous or next step.
Context Provider
import React, { useEffect, useState, createContext } from 'react'
const carSpecs = {
make: '', features: [],model: '',serviceHistory: false, warranty: false, trim: '', bodyType: '', transmission:''
}
export const UsedCarListingContext = createContext({})
export function GlobalUsedCarListingProvider (props) {
const [usedCar, setUsedCar] = useState(carSpecs)
useEffect(() => {}, [usedCar])
return (
<UsedCarListingContext.Provider
value={[usedCar, setUsedCar]}
>
{props.children}
</UsedCarListingContext.Provider>
)
}
Car Details Component
export const UsedCarAdDetails = () => {
const [usedCar, setUsedCar] = useContext(UsedCarListingContext)
const changeHandler = (e) => {
const {name, value} = e.target
setUsedCar({
...usedCar,
[name]: value
})
}
const handleChange = ({target: {name, checked}}) => {
setUsedCar({
...usedCar,
[name]: checked
})
}
return (
<div className="container-1100 bg-white shadow-nav-bar w-full h-52 pt-12">
<NavLink to={'/'} className='landing-nav-logo'>
<img
className='mr-20 mt-4 w-66 ottobay-logo-center'
src={OttobayGray}
alt={'logo'}
/>
</NavLink>
</div>
<div className="container-1050 mg-0auto flex justify-between mt-48">
<div className='container-700 p-20'>
<form>
<div className='ad-listing-input-wrapper mb-16-mobile mb-16 w-full-mobile flex items-center'>
<div className='ad-label-container'>
<label className="listing-input-label font-semibold mr-40"
htmlFor="videoLink">History: </label>
</div>
<div className="ad-input-group-container">
<div className='checkbox-group-container'>
<CheckboxWithImage
onChange={handleChange}
name={'serviceHistory'}
checked={usedCar.serviceHistory}
label={'Service History'}
icon={<GiAutoRepair/>}
checkboxTitleClass={'historyCB'}
/>
<CheckboxWithImage
onChange={handleChange}
name={'warranty'}
checked={usedCar.warranty}
label={'Warranty'}
icon={<AiOutlineFileProtect/>}
checkboxTitleClass={'historyCB'}
/>
</div>
</div>
</div>
<div>
<div className='checkbox-group-wrapper'>
{carFeatures.map(item => (
<div className='feature-item'>
<Checkbox
label={item.name}
onChange={handleFeaturesChange}
checked={usedCar && usedCar.features.some(val => val === item.id)}
value={item.id}
/>
</div>
))}
</div>
</div>
<div className="error-container"></div>
</div>
<div className="car-basic-submission-container">
<div> </div>
<button type='submit' className='search-button bg-button-primary text-white font-semibold rounded-4'> Next Step</button>
</div>
</form>
</div>
)
}
You seem to be calling a non existent function handleFeaturesChange in you feature-item checkbox.
Anyway, something like this should work:
const handleFeaturesChange = ({target: {value, checked}}) => {
setUsedCar({
...usedCar,
features: checked ? [
...usedCar.features,
value, // add the value to previously selected features
] : usedCar.features.filter(val => val !== value) // remove the value
})
}
You could potentially replace the value with name string but then you'd need to update the condition in the checked param of the Checkbox to compare it with the name instead.
I'm trying to add a search bar to a parent component.
All the logic is working fine in the console. With every character that is typed in the search field I get fewer results.
I try to pass it to a child component to render the card(s) result, but I get a blank card: I can not see data passed.
Parent Component <AllAssets>
class AllAssets extends Component {
state = {
cards: [],
searchField: '',
}
async componentDidMount() {
const { data } = await cardService.getAllCards();
if (data.length > 0) this.setState({ cards: data });
}
addToFavorites = (cardId, userId) => {
saveToFavorites(cardId, userId)
toast.error("The asset was added to your favorites.")
}
render() {
const { cards, searchField } = this.state;
const user = getCurrentUser();
const filteredAssets = cards.filter(card => (
card.assetName.toLowerCase().includes(searchField.toLowerCase())));
console.log(filteredAssets);
return (
<div className="container">
<SearchBox placeholder={"Enter asset name..."}
handleChange={(e) => this.setState({ searchField: e.target.value })}
/>
<PageHeader>Assets available for rent</PageHeader>
<div className="row">
<div className="col-12 mt-4">
{cards.length > 0 && <p>you can also add specific assets to your favorites and get back to them later...</p>}
</div>
</div>
<div className="row">
{!!filteredAssets.length ? filteredAssets.map(filteredAsset => <SearchResult addToFavorites={this.addToFavorites} filteredAsset={filteredAsset} user={user} key={filteredAsset._id} />) :
cards.map(card => <CardPublic addToFavorites={this.addToFavorites} card={card} user={user} key={card._id} />)
}
</div>
</div >
);
}
}
export default AllAssets;
Child Component <SearchResult>
const SearchResult = (addToFavorites, filteredAsset, card, user) => {
return (
<div className="col-lg-4 mb-3 d-flex align-items-stretch">
<div className="card ">
<img
className="card-img-top "
src={filteredAsset.assetImage}
width=""
alt={filteredAsset.assetName}
/>
<div className="card-body d-flex flex-column">
<h5 className="card-title">{filteredAsset.assetName}</h5>
<p className="card-text">{filteredAsset.assetDescription}</p>
<p className="card-text border-top pt-2">
<b>Tel: </b>
{filteredAsset.assetPhone}
<br />
<b>Address: </b>
{filteredAsset.assetAddress}
</p>
<p>
<i className="far fa-heart text-danger me-2"></i>
<Link to="#" className="text-danger" onClick={() => addToFavorites(card._id, user._id)}>Add to favorites</Link>
</p>
</div>
</div>
</div>
);
}
export default SearchResult;
When I console.log(filteredAsset) in <SearchResult> I get an empty object. What am I doing wrong?
This line is incorrect:
const SearchResult = (addToFavorites, filteredAsset, card, user) => {
You are passing in positional arguments, not named props. Do this instead:
const SearchResult = ({addToFavorites, filteredAsset, card, user}) => {
In your original code, React attaches all of your props as fields on the first argument. So they would be accessible in the child, but not in the way you're trying to access them. Try logging out the values of each of the arguments in the child, if you're curious to see what happens.
The corrected version passes in a single object with field names that match the names of your props. It's shorthand that's equivalent to:
const SearchResult = (
{
addToFavorites: addToFavorites,
filteredAsset: filteredAsset,
card: card,
user: user,
}
) => {
I making a react project and while data loading i need take my datas from json with fetch-get. But while i doing it its sending empty array in first then 1-2second later sending me datas. So while its empty data my render is starting so my application is crashing. How can i make it without get empty data ? My codes :
const { id } = useParams();
const [articles, setArticles] = useState([]);
const [collection, setCollection] = useState([]);
useEffect(() => {
fetch("http://localhost:3001/api/v1/article/bycollection/" + `${id}`)
.then((x) => x.json())
.then((z) => {
setArticles(z);
}, []);
fetch("http://localhost:3001/api/v1/collection/" + `${id}`)
.then((x) => x.json())
.then((z) => {
setCollection(z);
}, []);
}, [id]);
Empty error line :
return (
<div className="tomiddle ">
<div className="mb-3">
<BreadCrumbComp data={breadCrumbData} />
</div>
<div className="collectionBg">
<div className="collectionHeader flex">
<div className="w-32 h-32 p-4 mt-2 text-5xl">
<i className={collection.icon}></i>
</div>
<div>
<h1 className="collectionTitle ">{collection.name}</h1>
<WriterGroupInfo groupData={collection} /> // Here i getting error.
Its sending empty collection
</div>
</div>)
WriterGroupInfo :
const WriterGroupInfo = (props) => {
const prop = props.groupData;
console.log("prop=>", props); // Showing empty array.
const articles = prop.articles.slice(0, 3);
const last = articles[articles.length - 1];
const lastAuthor = articles.length > 1 ? " ve " + last.author.name : "";
const authorNames =
articles
.slice(0, 2)
.map((a) => a.author.name)
.join(", ") + lastAuthor;
return (
<div className="flex pt-4 ">
<Avatar.Group>
{articles.map((x) => (
<Avatar key={x.id} src={x.author.avatar} />
))}
</Avatar.Group>
<div className="text-collection-small-500 truncate text-xs pl-2 pt-1">
<span>Bu koleksiyonda {prop.articles.length} makale mevcut</span>
<br />
{articles.length > 0 ? (
<span className="truncate mr-2">
<span className="text-gray-400 ">Yazarlar: </span>
{authorNames}
</span>
) : (
<></>
)}
</div>
</div>
);
};
This is happening because you are setting the first value of collecion as an empty array:
const [collection, setCollection] = useState([]);
You should handle this situations like this:
return (
<div className="tomiddle ">
<div className="mb-3">
<BreadCrumbComp data={breadCrumbData} />
</div>
{collection.length === 0 ?
<div> No data for collection </div> :
<div className="collectionBg">
<div className="collectionHeader flex">
<div className="w-32 h-32 p-4 mt-2 text-5xl">
<i className={collection.icon}></i>
</div>
<div>
<h1 className="collectionTitle ">{collection.name}</h1>
<WriterGroupInfo groupData={collection} /> // Here i getting error.
Its sending empty collection
</div>
}
</div>)
Your callback inside useEffect has 2 async calls and you are not waiting for them to finish. They take their time. Before that the next render has already taken place and for that reason you have your data empty for some time until your fetch(and subsequent setState) is completed.
Since both fetch are independent you can wait for them using Promise.all and await.
useEffect( () => {
async yourFunction() {
let promise1 = fetch("http://localhost:3001/api/v1/article/bycollection/" + `${id}`).then((x) => x.json()).then((z) => {
setArticles(z);
});
let promise2 = fetch("http://localhost:3001/api/v1/collection/" + `${id}`).then((x) => x.json())
.then((z) => {
setCollection(z);
});
let results = await Promise.all([promise1,promise2]);
}
yourFunction();
}, [id]);
Also, [] as an extra parameter for the second then() is not required in my opinion.
The issue is UseEffect works asynchronously and it triggers after the initial render. So at the initial render, it tries to populate using null/Empty array values. You can initiate the passing array values using useState like const[value,setValue]=useState([]) .
I have built this modal component using react hooks. However the data that the modal shows when it pops up its incorrect (it always shows the name property for last element in the array).
//Modal.js
import ReactDOM from 'react-dom';
const Modal = ({ isShowing, hide, home_team }) => {return isShowing ? ReactDOM.createPortal(
<React.Fragment>
<div className="modal-overlay"/>
<div className="modal-wrapper">
<div className="modal">
<div className="modal-header">
<a>Home team: {home_team}</a>
<button type="button" className="modal-close-button" onClick={hide}>
</button>
</div>
</div>
</div>
</React.Fragment>, document.body
) : null;}
export default Modal;
// Main component
const League = ({ league, matches }) =>{
const {isShowing, toggle} = useModal();
return (
<Fragment>
<h2>{league}</h2>
{
matches.map((
{
match_id,
country_id,
home_team
}
) =>
{
return (
<div>
<p>{match_id}</p>
<button className="button-default" onClick={toggle}>Show Modal</button>
<a>{home_team}</a>
<Modal
isShowing={isShowing}
hide={toggle}
home_team={home_team}
/>
<p>{home_team}</p>
</div>
)
})
}
</Fragment>
)};
This is what matches data set looks like:
[{
match_id: "269568",
country_id:"22",
home_team: "Real Kings"
},
{
match_id: "269569",
country_id:"22",
home_team: "Steenberg United"
},
{
match_id: "269570",
country_id:"22",
home_team: "JDR Stars "
},
{
match_id: "269571",
country_id:"22",
home_team: "Pretoria U"
},
]
I am not sure whats going on because the data seems to be passed fine.
<p>{home_team}</p>
in the main component is showing everytime the expected property, however the Modal always shows the last home_team item in the array (e.g.Pretoria U)
you need to call useModal inside of the map function. otherwise you will open on toggle all Modals and the last one overlaps the others
const HomeTeam = ({ match_id, country_id, home_team }) => {
const {isShowing, toggle} = useModal();
return (
<div>
<p>{match_id}</p>
<button className="button-default" onClick={toggle}>Show Modal</button>
<a>{home_team}</a>
<Modal
isShowing={isShowing}
hide={toggle}
home_team={home_team}
/>
<p>{home_team}</p>
</div>
)
}
const League = ({ league, matches }) => (
<Fragment>
<h2>{league}</h2>
{ matches.map((match) => <Hometeam {...match} /> }
</Fragment>
);