How can I orderBy a component in React - javascript

I am rendering a component that display an array and I want them to be order by the last entry.
I've tried to do a function outside the render that orderBy and include it in the component but it's not mounting correctly.
orderBy() {
let data = this.state;
data.data.sort((a, b) => {
return a.published_on - b.published_on
}).map((data) => {
return data;
})
}
render() {
const { loading, data } = this.state;
return (
<div className="list">
<Table rowKey="ref" loading={loading} dataSource={data.data} size="large" />
</div>
);
}
I want the Table component to be order on the loading by data.data.published_on.
What is the correct way to do that in React ?

1) You're not returning anything from the function.
2) Since you're using class components you'll need to call it with this.orderBy
3) Instead of grabbing data from the state pass it into the function instead:
orderBy(data) {
return data.sort((a, b) => a.published_on - b.published_on);
}
render() {
const { loading, data } = this.state;
return (
<div className="list">
<Table rowKey="ref" loading={loading} dataSource={this.orderBy(data.data)} size="large" />
</div>
);
}

Related

Load same instance of a component in another component onClick in reactJs

I have two components smallViewComponent.jsx and fullViewComponent.jsx. In smallViewComponent.jsx, there are 3 instances of <AbcComp>.
So, the requirement is how to get these <AbcComp> instance on button click loadInstance() and render the different instances of <AbcComp> in fullViewComponent.jsx.
smallViewComponent.jsx
import AbcComp from './..'
this.state = {
cmpInstance = ['component1','component2','component3'];
}
render (){
return (
<div>
this.state.cmpInstance.map((e, index) =>
return (
//'component1','component2','component3' instance of AbcComp
<AbcComp key={e} btnProps={e}/>
)
</div>
)
}
AbcComp.jsx
render(){
// based on btnProps some diffrent color is added here
return (<div><Button className={this.props.btnProps}/></div>)
}
fullViewComponent.jsx
loadInstance =() => {
let cmpInstance = ['component1','component2','component3'];
return cmpInstance[(Math.random() * cmpInstance .length) | 0]
}
render (){
<button onClick={(e) => this.loadInstance()}></button>
// dynamically load 'component1','component2','component3' instance of AbcComp below.
<AbcComp />
}

JSX Function doesn't show return

As the title says, I can not figure out why the return of a function doesn't show on screen.
The object words.First.wordsdata is holding key-value pairs
import React from "react";
const WordList = ({ words }) => {
return (
<div>
{ words &&
Object.entries(words.First.wordsdata).forEach(([key, value]) => {
return(<div>{key} - {value}</div>);
})
}
</div>
);
};
export default WordList;
If I change the return to log it out, then
this one does show everything correctly in dev tools
return(console.log(key, value));
To give a full view, this is the file that calls the component
class Words extends Component {
render() {
//console.log(this.props)
const { words, auth } = this.props;
if (!auth.uid) return <Redirect to="/" />;
return (
<div>
<WordList words={words} />
</div>
);
}
}
I tried changing the return to simple HTML and It still doesn't show anything
Thank you for the answers, indeed changing .forEach to .map did the trick.

Sorting redux store array with sortBy redux action does not send new props to component

I am currently trying to build a sortby function which needs to sort an array with venues in the redux store based on alfabetical order. A live demo can be found here: http://tabbs-web-app.herokuapp.com/discover/home
Paste bins
Home.jsx
VenueList.jsx
VenueListFilter.jsx
VenueListItem
Venue Selector / filter function
Filters Action Generactor (redux)
Filters Reducer (redux)
Description
Unfortunately I am not able to upload the project to codesandbox since it's exceeding the 120 limit modules for sandbox...
The following component is retrieving the array of venues from the API and saves them to the redux store. This component is connected with the redux store and has the following redux functions to filter the array:
const mapStateToProps = (state) => {
return {
venues: getFilteredVenues(state.venues, state.filters)
};
};
const mapDispatchToProps = (dispatch) => ({
startSetVenues: () => dispatch(startSetVenues())
});
Home.jsx
render() {
return (
<div>
<SectionTitle
title="Discover Nightlife with Tabbs."
subTitle="Instant jaccess to your favorite nightclubs, lounge, bars and parties
nationwide."
/>
<VenueListFilter data={filterOptions} />
<VenueList data={this.props.venues} />
</div>
);
}
The following component is a child component which needs the data from the connected component:
VenueList.jsx
render({ data } = this.props) {
return (
<div>
<Grid container spacing={8}>
{data.map((venue, key) => {
return <VenueListItem key={key} venue={venue} />;
})}
</Grid>
</div>
);
}
The last component is the individual venue row / object / component:
VenueListItem.jsx
<Grid item {...rest} className={classes.grid}>
<ImageCard
image="https://static.bab.la/pic/living/UK/going-out-dancing.jpg"
cardTitle={venue.venue_name}
cardSubtitle={venue.venue_description}
content="test, abc, def"
/>
</Grid>
what is the issue I am experiencing right now?
The redux store is correctly being ordered based on alfabetical order. The mapStateToProps function is being called in the Home.jsx with the correct ordered array. But.. the components render function is not being called again, so the VenueList does not receive the new props / updated values.
Additional information
Sort / selector function:
const getFilteredVenues = (venues, { sortBy }) => {
return venues.sort((a, b) => {
if (sortBy === "alfabetical") {
return a.venue_name.toLowerCase() > b.venue_name.toLowerCase();
} else {
return 0;
}
});
};
Redux version: ^3.7.2
React version: 16.2.0
sort does inplace array modification. Just make a copy.
const getFilteredVenues = (venues, { sortBy }) => {
return venues.slice(0).sort((a, b) => {
if (sortBy === "alfabetical") {
return a.venue_name.toLowerCase() > b.venue_name.toLowerCase();
} else {
return 0;
}
});
};

How to prevent component from rendering child until data fetches

I have a Dashboard component that renders an array of cards with data fetched from a backend server. Users can create additional cards by submitting a form, which then redirects them back to the dashboard page.
My issue is that when the form is submitted, a javascript error 'cannot read property "includes" of undefined' is thrown and the dashboard does not render. If I manually refresh the page, the list renders as expected with the new card. I use Array.includes method to filter the cards based on the filterText state value. Does this error happen because the data has not been fetched when render is called? If so, how can I force the component to wait until there is data before rendering? Please see the components and redux action below.
const CardList = (props) => {
const cards = props.cards.map(({ _id, title}) => {
return (
<Card key={_id} title={title} />
)
});
return (
<div className="container">
<input onChange={ (e) => props.handleChange(e.target.value) } />
<div className="row">
{cards}
</div>
</div>
);
}
export default CardList;
export class Dashboard extends Component {
constructor(props) {
super(props);
this.state = {
filterText: ''
}
}
componentDidMount() {
this.props.fetchCards();
}
handleChange = (filterText) => {
this.setState({filterText});
}
render() {
const cardList = this.props.cards.filter(card =>
card.title.includes(this.state.filterText.trim().toLowerCase())
);
return (
<div>
<CardList cards={cardList}
handleChange={filterText => this.handleChange(filterText)} />
</div>
);
}
};
function mapStateToProps({ cards: { cards }}) {
return {
cards,
}
}
export default connect(mapStateToProps, {fetchCards})(Dashboard);
export class SurveyForm extends Component {
render() {
return (
<div>
<form>
<Field component={CardField} type="text"
label={'title'} name={'title'} key={'title'} />
<Button type="submit" onClick={() => submitCard(formValues, history)}>Next</Button>
</form>
</div>
);
}
}
REDUX ACTION DISPATCHER:
export const submitCard = (values, history) => async dispatch => {
const res = await axios.post('/api/cards', values);
try {
dispatch({ type: SUBMIT_CARD_SUCCESS, payload: res.data });
dispatch({ type: FETCH_USER, payload: res.data })
}
catch(err) {
dispatch({ type: SUBMIT_CARD_ERROR, error: err });
}
history.push('/cards');
}
Similar to what #JasonWarta mentioned, it's worth noting that React does not render anything when false, null, or undefined is returned, so you can usually use && to be more succinct than using the conditional ("ternary") operator:
render() {
return this.props.cards && (
<div>
<CardList
cards={this.props.cards.filter(card => card.title.includes(this.state.filterText.trim().toLowerCase())}
handleChange={filterText => this.handleChange(filterText)}
/>
</div>
);
}
Because && short-circuits, the latter part won't be evaluated so you can avoid TypeErrors, and the component will also render no content (same as when you return null).
I've used ternary operators in this kind of situation. You may need to adjust the check portion of the pattern, depending on what your redux pattern is returning. null value is returned if this.props.cards is falsey.
render() {
return (
{this.props.cards
?
<div>
<CardList
cards={this.props.cards.filter(card => card.title.includes(this.state.filterText.trim().toLowerCase())}
handleChange={filterText => this.handleChange(filterText)}
>
</CardList>
</div>
:
null
}
);
}
As an alternative to other answers you can return something else suitable if there is no data in your render function with an if statement. I prefer moving functions like your filter one outside of render. Maybe one other (better?) approach is doing that filter in your mapStateToProps function.
Also, if I'm not wrong you don't need to pass anything to your handleChange function. Because you are getting filterText back from CardList component then setting your state.
cardList = () => this.props.cards.filter(card =>
card.title.includes(this.state.filterText.trim().toLowerCase()));
render() {
if ( !this.props.cards.length ) {
return <p>No cards</p>
// or return <SpinnerComponent />
}
return (
<div>
<CardList cards={this.cardList()}
handleChange={this.handleChange} />
</div>
);
}

Update component when prop value changes

I have an object with the property home.ready = false. When the object is done getting data, cleaning it etc it changes to home.ready= true.
I need my component to register the change and update. My component:
class HomeNav extends React.Component {
render() {
let data = this.props.data;
let uniqueTabs = _.uniq(_.map(data, x => x.tab)).sort();
let tabs = uniqueTabs.map((tab, index) => {
let itemsByTab = _.filter(data, (x => x.tab == tab));
return <Tabs key={tab} tab={tab} index={index} data={itemsByTab} />;
});
console.log(this.props)
return (
<section>
<div className="wb-tabs">
<div className="tabpanels">
{ this.props.ready ? {tabs} : <p>Loading...</p> }
</div>
</div>
</section>
)
}
};
ReactDOM.render(
<HomeNav data={home.data.nav} ready={home.ready}/>,
document.getElementById('home-nav')
);
This is the home object. It's a simple object that gets data and once the data is ready the property ready changes from false to true. I can't get React to recognize that change. And at times React will say home is undefined.
Since you didn't post any code around the request, or data formatting, I will assume you got all that figured out. So, for your component to work the way it is currently written, you need to drop the curly braces around tabs ({ this.props.ready ? tabs : <p>Loading...</p> }), then, this.props.data should always contain a valid Array, otherwise it will break when you try to sort, filter, etc.
Or, you can do an early dropout, based on the ready property:
class HomeNav extends React.Component {
render() {
if(!this.props.ready){
return <section>
<div className="wb-tabs">
<div className="tabpanels">
<p>Loading...</p>
</div>
</div>
</section>
}
let data = this.props.data;
let uniqueTabs = _.uniq(_.map(data, x => x.tab)).sort();
let tabs = uniqueTabs.map((tab, index) => {
let itemsByTab = _.filter(data, (x => x.tab == tab));
return <Tabs key={tab} tab={tab} index={index} data={itemsByTab} />;
});
console.log(this.props)
return (
<section>
<div className="wb-tabs">
<div className="tabpanels">
{tabs}
</div>
</div>
</section>
)
}
};

Categories