I'm having an issue getting my child component to update with new props. I'm deleting an item from the global state and it's successful. But when the item gets deleted, I'm expecting that item to no longer show. It's still being shown but if I were to move to another screen then back, it's gone. Any idea on what I might be missing here?
Thanks!!
export default class Summary extends Component {
constructor(props) {
super(props);
this.state = {
pickupData: this.props.pickup
};
}
handleDelete(item) {
this.props.deleteLocationItem(item);
}
render() {
const pickup = this.state.pickup;
return (
<View>
{pickup.map((item, i) => (
<LocationItem
name={item}
onPressDelete={() => this.handleDelete(item)}
/>
))}
</View>
);
}
}
const LocationItem = ({ onPressDelete, name }) => (
<TouchableOpacity onPress={onPressDelete}>
<Text>Hi, {name}, CLICK ME TO DELETE</Text>
</TouchableOpacity>
);
------- Additional Info ------
case 'DELETE_LOCATION_INFO':
return Object.assign({}, state, {
pickup: state.pickup.filter(item => item !== action.action)
})
export function deleteLocationInfo(x){
return {
type: DELETE_LOCATION_INFO,
action: x
}
}
Your deleteLocationItem must be something like this:
deleteLocationItem(id) {
this.setState({
items: this.state.items.filter(item => item.id !== id)
});
}
Then inside your Summary class you dont need to set the prop again. Just receive pickup from props like this:
render (
const { pickup } = this.props;
return(
<View>
{ pickup.map
...
Render is happening based on the state which is not updated other than in constructor. When the prop updates from parent, it is not reflected in the state.
Add componentWillReceiveProps method to receive new props and update state, which will cause new data to render
But more preferably, if the state is not being changed in any way after initialization, render directly using the prop itself which will resolve this issue
Related
im new in react-native, im passing data by navigation to my edit_note screen, once i received i set it to my states, but it doesnt work, if i print them, it shows their values, but setting to my states doesnt work, heres the code:
heres the Notes class, in the navigation function im passing the datas, data and note_number to Edit_note
render() {
return (
<>
<View style = {this.styles.View}>
<FlatList data = {this.props.data} renderItem = {({item}) => (<TouchableOpacity onPress = {() => this.props.navigation.navigate("Edit_note", {data: this.props.data, note_number: item.note_number})}><Text style = {this.styles.Text}>{item.title}</Text></TouchableOpacity>)} keyExtractor = {(item) => item.note_number.toString()}></FlatList>
</View>
</>
);
}
in Edit_note im receiving it like this:
class Edit_note extends Component {
constructor() {
super();
this.state = {
array_notes: [],
note_number: "",
}
}
componentDidMount() {
const {params} = this.props.navigation.state;
let x = params.note_number;
this.setState({note_number: x});
console.log(this.state.note_number);
}
render() {
return (
<Text></Text>
);
}
}
if i print x, it will print the note_number, but setting it into note_number, and print it, it doesnt show anything, why?
You actually set it but your console.log() fires before the change.
After a state changes component rerenders so you can try printing it on screen like;
render() {
return (
<Text>{this.state.note_number}</Text>
);
}
setState is asynchronous that means that the state indeed changes but the console.log right after the setState is not showing the change.
You can learn more about it here.
When using a FlatList Component in react native I need to know when all the visible items have been rendered.
I am providing data and the renderItem when I get the componentDidMount I can see the FlatList there but because FlatList Component asynchronously renders each item they only show up after on componentDidUpdate. Also, this could (and probably will) include off-view items.
I would like to know if there is someone out there that has found a possible way to have a controlled process (not with setTimeout) of knowing when the visible items are fully rendered.
Thanks.
I ended up using the componentDidMount of the renderItem component as an indicator that element is rendered.
In the below example ActivityIndicator will be shown until the first 10 items are rendered (the number I set in the initialNumToRender).
This approach is not ideal. You may need to tweak it for your case but the general idea is shown below.
Here is the list item:
class ListItem extends React.Component<{ onRendered: () => void }> {
componentDidMount(): void {
this.props.onRendered();
}
render() {
return (
<View>
<Text>Item</Text>
</View>
);
}
}
And here is the screen with FlatList that uses above ListItem:
export class FlatListTestScreen extends React.Component<any, { creating: boolean }> {
constructor(props: any, context: any) {
super(props, context);
this.state = {
creating: true,
};
}
onRenderedItem = () => {
this.setState({
creating: false,
});
};
renderItem = (item: any) => {
return <ListItem onRendered={this.onRenderedItem} />;
};
dataArray = Array(20)
.fill('')
.map((_, i) => ({ key: `${i}`, text: `item #${i}` }));
render() {
const loader = this.state.creating ? <ActivityIndicator /> : <></>;
return (
<View>
{loader}
<FlatList
initialNumToRender={10}
data={ this.dataArray }
renderItem={ this.renderItem }
keyExtractor={ item => item.key }
/>
</View>
);
}
}
I also tried to use a map to collect the number of rendered items but on practice it seems that they all fire componentDidMount at the same time so simpler approach may be better.
I have three files: ShopsContainer.js ShopsComponent.js and ShopsItemComponent.js
ShopsContainer maintains an array of shop items in local state that gets passed down into ShopsComponent as props. ShopsComponent then maps through the items array that is being received as props and renders a ShopsItemComponent for each item in the array.
Within my ShopsContainer file, I have a method that removes a shop item from state using the following code:
removeShop = (shopAccount) => {
this.setState(prevState => ({
items: prevState.items.filter(shop => {
return shop.shopAccount !== shopAccount
})
}));
}
When this happens, the correct item is removed from the items array in state, however, whatever the last ShopItem is that is in the DOM at the time of the removeShop call will get removed no matter if it is the correct item that should be removed or not. In other words, when removeShop gets called and the items array in state gets updated correctly, the wrong ShopItemComponent gets removed from the DOM.
What I would like to happen (or what I think should happen) is when removeShop gets called, that shop gets removed from the items array in state and ShopsContainer re-renders causing ShopsComponent to re-render with the updated props being received. And lastly ShopsComponent would map through the newly updated items array in props displaying a `ShopItemComponent for the correct items. Perhaps the problem has to do with the props being updated?
My code is as follows:
ShopsContainer.js
class ShopsContainer extends Component {
constructor() {
this.state = {
items: null
}
this.getAll();
this.removeShop = this.removeShop.bind(this);
}
getAll = () => {
// API request that fetches items and updates state
}
removeShop = (shopAccount) => {
this.setState(prevState => ({
items: prevState.items.filter(shop => {
return shop.shopAccount !== shopAccount
})
}));
}
render() {
return (
<div>
{this.state.items ? <ShopComponent items={this.state.items} removeShop={this.removeShop} /> : <div><h1>Loading...</h1></div>}
</div>
);
}
}
ShopsComponent.js
class ShopsComponent extends Component {
constructor() {
this.handleRemove = this.handleRemove.bind(this);
}
handleRemove = (shopAccount) => {
this.props.removeShop(shopAccount);
}
render() {
return (
<React.Fragment>
<Header />
{this.props.items.map((shopItem, i) => {
return (<ShopItemComponent key={i} item={shopItem} removeShop={this.handleRemove} />);
})}
</React.Fragment>
);
}
}
Your code is working great, but you only has one mistake , your ShopComponent is assign index as a key for each ShopItemComponent and react is tracking those indexes to update the correct component, so you need to set key as a unique value between items, then I realize that shopAccount should be your id for each item.
The solution code is below.
class ShopsComponent extends Component {
handleRemove = (shopAccount) => {
this.props.removeShop(shopAccount);
}
render() {
return (
<React.Fragment>
<Header />
{this.props.items.map((shopItem) => <ShopItemComponent key={shopItem.shopAccount} item={shopItem} removeShop={this.handleRemove} />)}
</React.Fragment>
);
}
}
I hope you can find useful.
Note, when you are using a arrow function into your class, don't bind that method into the constructor, so remove it, because
handleRemove = (shopAccount) => {
this.props.removeShop(shopAccount);
}
is already binded.
I'm working on a React Native menu with a StackNavigator. If the user press a ListItem an id should be passed to all other Tabs in this menu, so the data can get fetched from an API. I tried to use screenProps to pass the data. Unfortunately I wasn't able to reset the value, when pressing a ListItem.
export default class Index extends Component {
constructor(props){
super(props);
}
render() {
return (
<OrderScreen
screenProps={ { Number: 123 } }
/>
);
}
}
In the child components I can access the prop but not reassign it:
export default class ListThumbnailExample extends Component
{
constructor(props)
{
super(props);
const{screenProps} = this.props;
this.state = { epNummer: screenProps.Number };
}
render()
{
return (
<Content>
<List>
{
this.state.orders.map(data => (
<ListItem key = {data.Number}
onPress = {() =>
{
this.props.screenProps.Number = data.Number;
this.props.navigation.navigate('Orders')
}
}
<Text>{ data.name }</Text>
</ListItem >
))
}
</List>
</Content >
);
}
}
Thank you!
In React and React-native props are immutable by design :
https://reactjs.org/docs/components-and-props.html#props-are-read-only
In your case if you want to pass screen-specific data you may wanna try passing them in the params of the navigation.navigate() function like this :
this.props.navigation.navigate('Orders',data.Number)
you can then access them in "Orders" screen from : props.navigation.state.params
More information here : https://reactnavigation.org/docs/params.html
I'm new in react-native and have some probems. In the fahterScreen I add some items to array and pass to childs as prop, I need that the child (CanastaScreen) update every 1seg and show the new value. I have the next code:
export default class CanastaScreen extends React.Component {
constructor(props) {
super(props);
setInterval( () => { this.render(); }, 1000);
};
render() {
return (
<Container>
<Content>
{this.props.screenProps.canasta.map( (item) => {
console.log(item.nombre);
return (
<Text>{item.nombre}</Text>
);
})}
</Content>
</Container>
);
}
}
Console output show correctly:
Item1
Item2
Item3
etc.
But the screen is always in blank. Some can help my about it ?
Thanks
First of all, you never should call render method of a component. in React Native, a component should update only if it's state changes. so if you have something like this :
<Parent>
<Canasta> ... </Canasta>
</Parent>
assuming that the changing variable is called foo in state of Parent, you need to pass it as prop to Canasta (child) and now by changing state of Parent (changing foo), Canasta should get updated. here's an example (calling updateFoo will update both Parent and Canasta):
class Parent extends Component {
constructor(props){
super(props); // it's recommended to include this in all constructors
this.state = { foo: initalValue } // give some value to foo
}
updateFoo(newValue){
this.setState({foo: newValue}) // setting state on a component will update it (as i said)
}
render() {
return(
<Canasta someProp={this.state.foo}> ... </Canasta>
)
}
}
}
After various changes, is found, the complete structure is: Parent(App.js) call children(Menu, Canasta). Menu allow add items to the shop-car and Canasta allow to sort and delete items. These are the important parts of the code:
App.js
export default class App extends React.Component {
constructor(props) {
super(props);
this.stateUpdater = this.stateUpdater.bind(this);
this.state = { canasta:[] };
}
render() {
return (
<View>
<RootNavigation data={[this.state, this.stateUpdater]} />
</View>
);
}
}
Menu.js
tryAddCanasta(index, plato){
let canasta = this.props.screenProps[0].canasta;
plato.id_Plato = canasta.length;
canasta.push(plato);
this.props.screenProps[1]('canasta', canasta);
}
Canasta.js
shouldComponentUpdate(nextProps, nextState) {
return true;
}
render() {
return (
<Container>
<Content>
<List>
{this.props.screenProps[0].canasta.map( (item) => {
return ( this._renderRow(item) );
})}
</List>
</Content>
</Container>
);
}
Special thanks to #Shadow_m2, now I don't need check every time, it works in "real time"