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.
Related
I am trying to add translation system in my app. My sample code:
async translate(txt){
var res = await translate(txt, {
//some props
})
return res[0] //it was returned as an object, so taking the first element
}
render(){
return (
<View>
<Text>{this.translate('Hello')}</Text>
</View>
)
}
I know, async returns a promise but I could not figure out a way to use it in my case.
I have checked other questions, but those didn't work :(
You would need to wait for the promise to resolve before displaying the output of translate so you could use a state to store the output once translate finishes.
constructor() {
super();
this.state = {};
}
componentDidMount() {
this.translate("Hello").then((output) => this.setState({ text: output }));
}
render() {
return (
<View>
<Text>{this.state.text ? this.state.text : "Loading"}</Text>
</View>
);
}
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 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
I have multiple layers of React components for getting an embed from a music service API, including a higher-order component that hits the API to populate the embed. My problem is that my lowest-level child component won't change state. I basically want the populated embed (lowest level component) to display an album cover, which disappears after clicking it (revealing an iframe), and whose state remains stable barring any change in props higher up (by the time this component is revealed, there should be no other state changes aside from focus higher up). Here's the code:
Parent:
return (
/*...*/
<Embed
embed={this.props.attributes.embed}
cb={updateEmbed}
/>
/*...*/
First child ( above):
render() {
const {embed, className, cb} = this.props;
const {error, errorType} = this.state;
const WithAPIEmbed = withAPI( Embed );
/*...*/
return <WithAPIEmbed
embed={embed[0]}
className={className}
cb={cb}
/>;
/*...*/
withAPI:
/*...*/
componentWillMount() {
this.setState( {fetching: true} );
}
componentDidMount() {
const {embed} = this.props;
if ( ! embed.loaded ) {
this.fetchData();
} else {
this.setState( {
fetching: false,
error: false,
} );
}
}
fetchData() {
/*... some API stuff, which calls the callback in the top level parent (cb()) setting the embed prop when the promise resolves -- this works just fine ...*/
}
render() {
const {embed, className} = this.props;
const {fetching, error, errorType} = this.state;
if ( fetching ) {
/* Return some spinner/placeholder stuff */
}
if ( error ) {
/* Return some error stuff */
}
return (
<WrappedComponent
{...this.props}
embed={embed}
/>
)
}
And finally the last child I'm interested in:
constructor() {
super( ...arguments );
this.state = {
showCover: true,
};
}
render() {
const {embed, setFocus, className} = this.props;
const {showCover} = this.state;
if ( showCover ) {
return [
<div key="cover-image" className={classnames( className )}>
<figure className='cover-art'>
<img src={embed.coverArt} alt={__( 'Embed cover image' )}/>
<i onClick={() => {
this.setState( {showCover: false,} );
}}>{icon}</i> // <-- Play icon referenced below.
</figure>
</div>,
]
}
return [
<div key="embed" className={className}>
<EmbedSandbox
html={iframeHtml}
type={embed.embedType}
onFocus={() => setFocus()}
/>
</div>,
];
}
My issue is that clicking the play icon should clear the album cover and reveal the iframe embed, but even though the click is registering, the state never changes (or does and then changes back). I believe it's because a higher-level component is mounting/unmounting and reinstantiating this component with its default state. I could move this state up the tree or use something like Flux, but I really feel I shouldn't need to do that, and that there's something fundamental I'm missing here.
The problem is that const WithAPIEmbed = withAPI( Embed ); is inside the render method. This creates a fresh WithAPIEmbed object on each render, which will be remounted, clearing any state below. Lifting it out of the class definition makes it stable and fixes the problem.