How to re-render Flatlist in React Native using componentWillRecieveProps? - reactjs

UNSAFE_componentWillMount(){
this.props.EmployeeFetch();
}
renderRow(employee){
return <ListItem employee={employee}/>;
}
render(){
return(
<FlatList style={{flex:1,height:100}}
data = {this.props.employees}
/>
);
}
}
const mapStateToProps=state=>{
const employees = _.map(state.employees,(val,uid)=>{
return {...val,uid};
});
return {employees};
}
export default connect(mapStateToProps, {EmployeeFetch})(EmployeeList);
Here I am fetching data from firebase. At first it came null and after some time data came. So, how will I re-render the new data using the Flatlist and componentWillRecieveProps()?

you should use 2 return function.
1 which return loading (activity indicator)
2 which return data if data exists in any state.
constructor(props) {
super(props);
this.state = { isLoading: true};
}
render() {
if(this.state.isLoading){
return(
<View>
<ActivityIndicator size="large" color="#0c9"/>
</View>
)}
return(
<FlatList style={{flex:1,height:100}}
data = {this.props.employees}
/>
);
const mapStateToProps=state=>{
const employees = _.map(state.employees,(val,uid)=>{
return {...val,uid};
});
if(employees){
this.state.isLoading = false;
}
return {employees};
}
note: do not forget to import react components
and also if you are using react navigation then try to use to navigation events for data fetch api rather than using UNSAFE_componentWillMount() or find some other solution.

So componentWillReceiveProps is deprecated, you should use getDerivedStateFromProps.
You can use it like this with your example:
static getDerivedStateFromProps(props, state) {
const { employees } = this.props;
return employees;
}
Then in your render:
render() {
const { employees } = this.props;
// then use map(employees)
}

Related

error Objects are not valid as react child in react native, when i call async function in render method

i am calling this function in Render method in React Native code, but it shows the error,
objects are not valid as react child
display = async () => {
a="Hellow";
return a;
}
render() {
return (
<View>
<Text>{await this.display()}</Text>
</View>
);
}
You don't need this.display to be async, so this could be written as
display = () => {
a = "Hellow";
return a;
}
render() {
const display = this.display();
return (
<View>
<Text>{display}</Text>
</View>
);
}
Furthermore, if you are trying to do something asynchronously to change what is rendered, it should be done in the componentDidMount method. This is where you would set the state for the component.
async componentDidMount {
...
const display = await fetchData(url);
this.setState({ display });
}
render() {
return (
<View>
<Text>{this.state.display}</Text>
</View>
);
}
A better way to do this would be to store the text you want to display in state in the componentDidMount function and then render it like this rather than running a function in the render method:
componentDidMount = () => {
a = "hellow"
this.setState({ text: a })
}
render () {
return (
<Text>{this.state.text}</Text>
)
}

React Native Search filter not working properly

I am trying to implement react native search filter. I have my data array on the parent screen and I applied the search filter on the parent file and passed the filtered data array as props to the child screen. But no filter is happening. I am confused or no clue what wrong i am doing. I have the following codes:
ParentScreen.js
import SearchInput, { createFilter } from 'react-native-search-filter';
...
const KEYS_TO_FILTERS = ['title']
export default class ProductScreen extends Component {
constructor(props) {
super(props)
this.state = {
items: API,
searchTerm: '',
}
}
searchUpdated(term) {
this.setState({ searchTerm: term })
}
render() {
const filteredItems = this.state.items.filter(createFilter(this.state.searchTerm, KEYS_TO_FILTERS))
let cardGridWithBodyProps = {
navigation: this.props.navigation,
api: filteredItems,
gridNumber: 2,
}
return (
<Container>
<ScrollView showsVerticalScrollIndicator={false}>
<View>
<Header>
<SearchInput
onChangeText={(term) => { this.searchUpdated(term) }}
placeholder="Search"
/>
</Header>
</View>
<View>
<CardGridWithBody {...cardGridWithBodyProps} />
</View>
</ScrollView>
</Container>
)
}
}
ChildScreen.js
export default class CardGridWithBody extends Component {
constructor(props) {
super(props)
this.state = {
items: this.props.api
}
}
renderGrid(gridArray) {
return gridArray.map((row, rowIndex) => (
<Row key={rowIndex}>
{row.map((col, colIndex) => (this.renderColumn(col, rowIndex,colIndex)))}
</Row>
));
}
renderColumn(colItem, rowIndex, colIndex) {
return (
<Col key={colIndex}>
<Text>{colItem.title}</Text>
</Col>
)
}
renderContent() {
let gridArray = this.state.items
return this.renderGrid(gridArray)
}
render() {
return (
this.renderContent()
)
}
}
Instead of saving the data in state, access it directly from props. If you save it in state, you'll need to update it manually using lifecycle methods such as shouldComponentUpdate or getDerivedStateFromProps
renderContent = () => {
let gridArray = this.props.api;
return this.renderGrid(gridArray);
}
In parent screen convert
searchUpdated(term) {
this.setState({ searchTerm: term })
}
to
searchUpdated = term => {
this.setState({ searchTerm: term })
}
and in your child component you can do
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.api !== prevState.api) {
return { api: nextProps.api };
}
return null;
}
componentDidUpdate(prevProps, prevState) {
if (
this.state.items !==
prevState.items
) {
this.setState({ items: api });
}
}

It is best way filter data inside to render?

I'm new at Redux. I try to filter my code and pass to other Router component page.
Is it best way to filter my data inside render method or i should do that anywhere else? And How Can i pass my props to other router page?
I Do following;
This one a first component page.
class Home extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
this.props.actions.getProgramsStart();
}
render() {
const { ProgramsLoading, programs } = this.props.state;
if(programs) {
const SeriesFilterData=[];
const MoviesFilterData =[];
programs.map(FilterPrograms => {
if(FilterPrograms.programType==="series" && FilterPrograms.releaseYear >= 2010){
SeriesFilterData.push(FilterPrograms);
}
if(FilterPrograms.programType==="movie" && FilterPrograms.releaseYear >= 2010){
MoviesFilterData.push(FilterPrograms);
}
});
}
return (
<div id="home">
{ ProgramsLoading ? <div><Loader style={{ display: "block" }} content="Program List loading" /></div> : <h1>program data</h1> }
</div>
);
}
}
const mapStateToProps = (state) => {
return {
state: {
...state.home
}
};
};
const mapDispatchToProps = (dispatch) => {
return {
actions: bindActionCreators(homeActions, dispatch)
};
};
Yes you can avoid entering the filters by returning something previously based on your ProgramsLoading, and then change your map which is returning an empty array and creating 2 additional arrays in each render for a reduce which will use an object with just the 2 arrays that you need, everything in 1 loop.
Also take into account that you call FilterPrograms to the variable of your map, and it is confusing, because it is the current program and FilterPrograms sounds more like a function instead.
class Home extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
this.props.actions.getProgramsStart();
}
render() {
const { ProgramsLoading, programs } = this.props.state;
//check if you are loading, so you dont need to apply filters or whatever you add (filter/map creates a new array each time)
if(ProgramsLoading) return <div><Loader style={{ display: "block" }} content="Program List loading" /></div>
const defaultValue = {SeriesFilterData: [], MoviesFilterData =[]}
const reducerFunction = (accum, currentValue)=>{
//if this check is an AND for all the cases, return directly if the item doesnt match.
if(currentValue.releaseYear < 2010) return accum;
if(currentValue.programType==="series"){
accum.SeriesFilterData.push(currentValue);
} else if(currentValue.programType==="movie"){
accum.MoviesFilterData.push(currentValue);
}
return accum;
}
const results = programs.reduce( reducerFunction, defaultValue);
// using {...result} will destructure to be (SeriesFilterData, MoviesFilterData) separeted props
return (
<div id="home">
<h1>program data</h1>
<SomeComponent {...resulst} />
</div>
);
}
}

How to pass spinning bar to another component in ReactJS

I am in a scenario where I have to add a spinning bar in the component say,
List.js
class List extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
//spinning bar should be displayed here
</div>
);
}}
But the spinning bar should be displayed when another method in Actions(i.e redux) is called. So How will I pass this from actions.js to the render component in List.js
Actions.js
export const getList = (listInfo) => dispatch => {
//Spinning should start here
return application.getClientInfo(userInfo).then(
listInfo => {
//spinning should stop here
return dispatch(getListInfo(listInfo))
},
error => {
return dispatch(apologize('Error in getting application'))
}
)
}
getList and ListComponent is called in main.js
main.js
render() {
this.props.getClientApplication(this.props.user);
return (
<div>
<List />
</div>
);
}
So how will I add render method here that is actually to be displayed in list.js? Please help
In your reducer, keep a loading state and dispatch an action to set and clear loading states as and when you want
class List extends Component {
constructor(props) {
super(props);
}
render() {
const { isLoading } = this.props;
return (
<div>
//spinning bar should be displayed here
{isLoading && <Spinner>}
</div>
);
}
}
Actions.js
export const spinner = isLoading => {
return {
type: actionType.SPINNER, isLoading
}
}
export const getList = (listInfo) => dispatch => {
//dispatch loading action
dispatch(spinner(true));
return application.getClientInfo(userInfo).then(
listInfo => {
dispatch(spinner(false))
return dispatch(getListInfo(listInfo))
},
error => {
dispatch(spinner(false))
return dispatch(apologize('Error in getting application'))
}
)
}
Also make sure you aren't dispatching an action in render without using suspense
render() {
this.props.getClientApplication(this.props.user);
return (
<div>
<List isLoading={this.props.isLoading} />
</div>
);
}

Performance drop when updating state

I'm building react-native app, but my problem is linked with React itself.
It's an app that connects to external JSON, fetches data, and creates react component for each of item in that JSON data, so it's like 70 child components inside 1 wrapper. App is also using Navigator and phone storage but that's a part of the question.
To visualize:
Parent component (People) has methods to operate on a DB, it fetches data, creates component for each of item in array and exposes methods to child components (Person). Each person has a "add to favourites" button, this is a method updating empty star to full one (conditional rendering) if clicked, updates state of component and whenever local state has been changed it fires parents component to update it's state - and from there parent's component saves all data to DB. So I've made a link to synchronize Child's State -> Parent's State -> Local DB (phone's memory).
Problem is, it's quite slow when updating parent's state. It freezes for 1-1.5 sec. but it's all cool if I remove method to update parent's state (I've marked that in example attached).
Question 1: How to refactor this code to fix performance issue when updating parent's (People's state)?
Question 2: I'm open to any other suggestions and lessons how to improve quality of my code.
Here's a link to visualize my code, I've just removed few non-relevant methods and styles.
https://jsfiddle.net/xvgfx90q/
class People extends React.Component {
constructor() {
super();
this.state = {
peopleData: [],
database: {}
}
}
componentDidMount() {
this.fetchApi();
this.syncDatabase();
}
// function that connects to external JSON file and parses it
fetchApi() {... it sets peopleData state after promise has been resolved}
// function called from PersonSection to pass it's state and update main state of People
syncStates(data) {
const newState = this.state;
newState.database[data.id] = data;
this.setState(newState); // <-- !! PERFORMANCE DROP HERE !!
this.saveDatabase();
}
// connects to phone's DB and updates state with result of promise
async syncDatabase() {
AsyncStorage.getItem(this.state.DBKey).then((data) => {
let newState = {};
newState.database = JSON.parse(data);
this.setState(newState);
}).catch((error) => {
return error;
})
}
// saves current state to DB
async saveDatabase() {
AsyncStorage.setItem(this.state.DBKey, JSON.stringify(this.state.database));
}
renderTeams() {
return Object.keys(this.state.peopleData).map((team) => {
return (
<TeamSection key={team} teamName={team} membersList={this.state.peopleData[team]}>
{this.renderPeople(team)}
</TeamSection>
)
})
}
renderPeople(team) {
return this.state.peopleData[team].map((people) => {
return (
<PersonSection
key={people.id}
data={people}
database={_.has(this.state.database, people.id) ? this.state.database[people.id] : false}
navigator={this.props.navigator}
syncStates={this.syncStates.bind(this)}
/>
)
})
}
render() {
return (
<ScrollView style={styles.wrapper}>
<Options filterPeople={this.filterPeople.bind(this)} />
{this.renderTeams()}
</ScrollView>
)
}
}
class PersonSection extends Component {
constructor(props) {
super(props);
this.state = {
database: {
id: this.props.data.id,
name: this.props.data.name,
favourites: this.props.database.favourites
}
}
}
// updates components state and sends it to parent component
toggleFavourites() {
const newState = this.state.database;
newState.favourites = !newState.favourites;
this.setState(newState);
this.props.syncStates(this.state.database);
}
render () {
return (
<View>
<View>
<View>
<Text>{this.props.data.name}</Text>
<Text>{this.props.data.position}</Text>
<Text>{this.props.data.ext}</Text>
</View>
<View>
<TouchableOpacity onPress={() => this.toggleFavourites()}>
{ this.state.database.favourites
? <Icon name="ios-star" size={36} color="#DAA520" />
: <Icon name="ios-star-outline" size={36} color="#DAA520" />}
</TouchableOpacity>
</View>
</View>
</View>
)
}
};
export default PersonSection;
React.render(<People />, document.getElementById('app'));`
This is not a recommended way to do it, but basically you can just update the child state instead of the parent and passing it back down.
class People extends React.Component {
constructor() {
super();
this.state = {
peopleData: [],
database: {}
}
}
componentDidMount() {
this.fetchApi();
this.syncDatabase();
}
// function that connects to external JSON file and parses it
fetchApi() {... it sets peopleData state after promise has been resolved}
// function called from PersonSection to pass it's state and update main state of People
syncStates(data) {
this.state.database[data.id] = data;
this.saveDatabase();
}
// connects to phone's DB and updates state with result of promise
async syncDatabase() {
AsyncStorage.getItem(this.state.DBKey).then((data) => {
let newState = {};
newState.database = JSON.parse(data);
this.setState(newState);
}).catch((error) => {
return error;
})
}
// saves current state to DB
async saveDatabase() {
AsyncStorage.setItem(this.state.DBKey, JSON.stringify(this.state.database));
}
renderTeams() {
return Object.keys(this.state.peopleData).map((team) => {
return (
<TeamSection key={team} teamName={team} membersList={this.state.peopleData[team]}>
{this.renderPeople(team)}
</TeamSection>
)
})
}
renderPeople(team) {
return this.state.peopleData[team].map((people) => {
return (
<PersonSection
key={people.id}
data={people}
database={_.has(this.state.database, people.id) ? this.state.database[people.id] : false}
navigator={this.props.navigator}
syncStates={this.syncStates.bind(this)}
/>
)
})
}
render() {
return (
<ScrollView style={styles.wrapper}>
<Options filterPeople={this.filterPeople.bind(this)} />
{this.renderTeams()}
</ScrollView>
)
}
}
class PersonSection extends Component {
constructor(props) {
super(props);
this.state = {
database: {
id: this.props.data.id,
name: this.props.data.name,
favourites: this.props.database.favourites
}
}
}
// updates components state and sends it to parent component
toggleFavourites() {
const newState = this.state.database;
newState.favourites = !newState.favourites;
this.setState(newState);
this.props.syncStates(this.state.database);
}
render () {
return (
<View>
<View>
<View>
<Text>{this.props.data.name}</Text>
<Text>{this.props.data.position}</Text>
<Text>{this.props.data.ext}</Text>
</View>
<View>
<TouchableOpacity onPress={() => this.toggleFavourites()}>
{ this.state.database.favourites
? <Icon name="ios-star" size={36} color="#DAA520" />
: <Icon name="ios-star-outline" size={36} color="#DAA520" />}
</TouchableOpacity>
</View>
</View>
</View>
)
}
};
export default PersonSection;
React.render(<People />, document.getElementById('app'));

Resources