Browser Back button does not fire componentDidMount() React life cycle method - reactjs

I have react with redux app the gets a list of order items from Rest API and put on array filled with redux. There is a link in the same list that navigates to the details of an order. When navigating to the details and press the browser back button in order to load the list again I got the following error.
TypeError: fetchedOrders.map is not a function
this is the code inside orders list container
componentDidMount() {
this.props.handleFetchOrders(1);
}
render() {
{ fetchedOrders && fetchedOrders.map(order => {
return (
<OrdersWE
order={order}
onDelete={onDelete}
history = {this.props.history}
key={order.OrderHID}
/>
);
})}
}
const mapDispatchToProps = dispatch => {
return {
handleFetchOrders: (pageNumber) => {
dispatch(fetchOrders(pageNumber));
}
};
};

You missed the return statement from render function, also you have to return a single ReactElement:
class App extends React.Component {
...
render() {
return (
<>
{fetchedOrders &&
fetchedOrders.map(order => {
return (
<OrdersWE
order={order}
onDelete={onDelete}
history={this.props.history}
key={order.OrderHID}
/>
);
})}
</>
);
}
}
Moreover, try to log fetchedOrders, according to the error it may be not an array object.

Related

Re render when data from async function is ready

I have an async function that GET the notes from network, the problem that I have is that it tries to render an empty array of data, therefore I get this error saying that item.id is undefined because the array is empty. I tried to put a condition if (data.length === 0) return <Text>No Entries</Text> but then it does not re render anything, even though when I console.log(data) I can see the data has arrived. Is there any way to re render when data has arrived, or any other way around this?
export default class NoteList extends Component {
render() {
const { data} = this.props;
return (
<View style={styles.cardView}>
<FlatList
numColons={data.length}
data={data}
renderItem={({ item: { name, content, id } }) => {
return (
<View>
<NoteCard
name={name}
content={content}
/>
</View>
);
}}
keyExtractor={(item) => item.id}
/>
</View>
);
}
}
How to prevent this:
TypeError: TypeError: undefined is not an object (evaluating 'item.id')
I also get this error, but I think it is related to the management of the first problem.
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory
leak in your application. To fix, cancel all subscriptions and asynchronous tasks in %s.%s, the componentWillUnmount method,
The problem you have, lies in the parent where data is saved in the state. Therefor you get the warning about updating the state of an unmounted component. There are a lot of different ways to fix this. One way I like (because of the readability), is using a variable for when the component mounts. A simple example:
class News extends Component {
_isMounted = false;
constructor(props) {
super(props);
this.state = {
news: [],
};
}
componentDidMount() {
this._isMounted = true;
axios
.get('https://hn.algolia.com/api/v1/search?query=react')
.then(result => {
if (this._isMounted) {
this.setState({
news: result.data.hits,
});
}
});
}
componentWillUnmount() {
this._isMounted = false;
}
render() {
...
}
}
Now when the data is set, the NoteList component will automatically get updated. However what happens when api call fails. To prevent stale data, I like to use conditional rendering. Just like you suggested:
export default class NoteList extends Component {
render() {
const { data } = this.props;
if (data) {
return (
<View style={styles.cardView}>
<FlatList
numColons={data.length}
data={data}
renderItem={({ item: { name, content, id } }) => {
return (
<View>
<NoteCard name={name} content={content} />
</View>
);
}}
keyExtractor={item => item.id}
/>
</View>
);
}
}
}
A common way to do this in React is to keep track of when data is being fetched. This can be done e.g. by having a isFetching field in your state:
// This would be your default state
this.state = {
isFetching: false
};
Then, when you fire off the request (preferably in componentDidMount) you set isFetching to true using:
this.setState({ isFetching: true });
And finally, when the data arrives, you set it to false again:
this.setState({ isFetching: false });
Now, in your render function you can do something like this:
render () {
return (
<div className="something">
<h3>Some content</h3>
{this.state.isFetching ? <LoadingComponent /> : (
<ul>
{listItems}
</ul>
)}
</div>
)
}
By using state, you don't have to worry about telling your component to do something, instead it reacts to changes in the state and renders it accordingly.

How to change attribute of a React Element

I've created a render method which adds a number of dynamically created 'InfoWindow' elements to a Class based object.
Each InfoWindow element has a unique ID and key.
I also have a number of 'Marker' elements with corresponding ids and keys.
Currently all Infowindows have a prop of 'visible={false}'
When I click a Marker a function is called which outputs the Marker ID.
I'd like to find the InfoWindow with the relevant ID and set visibility = {true}
Is there a way to find the relevant InfoWindow element using its key or ID, and then call setAttribute (or equivalent)?
I've tried searching the DOM for the ID but Google Maps doesn't render that way, so I'm thinking there must be a more React-y way to do this?
let visibilityFunction = () => {
this.changeVisibility(01);
};
changeVisibility = (e) => {
console.log(e);
//this currently outputs the ID (01)
}
render() {
return(
<Parent>
<InfoWindow
visible={false}
key={01-iw}
id={01-iw}
/>
<Marker
key={01}
id={01}
onClick={visibilityFunction}
/>
</Parent>
);
}
Like I was saying in the comments. Use state here to update the visibility.
class MyComponent extends React.Component {
state = { visibleWindows: {}, currentWindows: [1] };
changeVisibility = id => {
this.setState(prevState => ({
visibleWindows: {
...prevState.visibleWindows,
[id]: !prevState.visibleWindows[id]
}
}));
};
render() {
const { currentWindows, visibleWindows } = this.state;
return (
<div>
{currentWindows.map(win => (
<ChildWindow key={win} id={win} isVisible={!!visibleWindows[win]} onChange={this.changeVisibility} />
))}
</div>
);
}
}
class ChildWindow extends React.Component {
changeVisibility = () => {
this.props.onChange(this.props.id)
}
render() {
<React.Fragment>
<InfoWindow
visible={this.props.isVisible}
key={`${win}-iw`}
id={`${win}-iw`}
/>
<Marker
key={win}
id={win}
onClick={this.changeVisibility}
/>
</React.Fragment>
}
}
Here's a rudimetary example for you to poke around with :)

How to reload the current component in react?

I've have an component "A" with a button. When the user press the button I'm showing a modal(react-responsive-modal) with bunch of filed and an update button. When the user presses the update button on the modal I want to reload the component "A" with the updated data.
I tried redirecting using this.props.history.push('dashboard/componentA') but it didn't work. Then I tried redirecting to the dashboard and again redirecting to the component like this
this.props.history.push('/dashboard');
this.props.history.push('/dashboard/componentA');
It worked but I'm not seeing any loader that I've used on 'componentWillMount' and the component just freezes up. I couldn't scroll up or down.
Try not to use the browser history as a way to update react (as much as you can). React is designed to re-render components when the props or state for that component change. As an example, this should trigger an update in ComponentA without needing to update the browser's history:
class ComponentA extends Component {
handleModalClick = (event) => {
this.setState({
componentData: event.data,
});
}
render() {
return (
<ReactModal onClick={this.handleClick} />
)
}
}
EDIT: Updated to show a data fetching parent component:
class DataFetcher extends Component {
saveAndFetchData = (saveData) => {
FetchDataPromise(saveData).then((updatedData) => {
this.setState({ data: updatedData });
}
}
render() {
const { data } = this.state;
return (
<div>
<ComponentA data={data} />
<ReactModalComponent handleClick={saveAndFetchData} />
</div>
);
}
}
class ComponentA extends Component {
render() {
const { data } = this.props;
return (
<div>
...render data...
</div>
)
}
}

MultiStep Form - Cannot update during an existing state transition error

I am trying to build a multi-stage form in React. I build a simple demo to try out the functionality of going through the form. As a template, I used this tutorial I found on the web.
The error message I am getting is
Warning: Cannot update during an existing state transition (such as within "render"). Render methods should be a pure function of props and state.
after I click on the continue button.
I looked through multiple StackOverflow questions, which had the same error message, but none helped me to resolve my issue.
MainForm
export class MainForm extends Component {
state = {
step: 1
};
nextStep = () => {
const {step} = this.state;
this.setState({
step: step + 1
});
};
prevStep = () => {
const {step} = this.state;
this.setState({
step: step - 1
});
};
render() {
const {step} = this.state;
switch (step) {
case 1:
return (
<PatientDetails
nextStep={this.nextStep}
/>
);
case 2:
return(
<Summary
prevStep={this.prevStep()}
/>
);
default:
return(
<h1>Something went wrong, kiddo</h1>
);
}
}
}
export default MainForm
PatientDetails
export class PatientDetails extends Component {
continue = e => {
e.preventDefault();
this.props.nextStep();
};
render() {
return (
<Form horizontal>
<Button bsStyle="primary" onClick={this.continue}>Continue</Button>
</Form>
);
}
}
export default PatientDetails;
Summary
export class Summary extends Component {
back = e => {
e.preventDefault();
this.props.prevStep();
};
render() {
return (
<Form horizontal>
<Button bsStyle="primary" onClick={this.back}>Continue</Button>
</Form>
);
}
}
export default Summary;
As described in the comments, the warning indicates that setState is (unintentionally) being called in the middle of the render call.
Looking at your code the culprit is to be found for when you attempt to render the Summary component as the handler you pass to it, is accidentally being called since you wrote prevStep={this.prevStep()}. By removing the parentheses and writing prevStep={this.prevStep} your issue should be fixed.

React Redux - element not rendering on page but React not throwing error

I seem to be having an issue with my react-redux app. I'm currently using next.js, which tends to act a little weird when working with redux so i'm not sure if that's the issue. That said, I'm trying to render a component that loops through an array of objects. very simple. my mapState function is working and when I set info to state.aboutMe[0] i received the first value. Once I remove this and try to iterate through the array, Initially, I got an error that says "A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object." but I was able to resolve that by wrapping my my info.map in a <div> el.
I checked out the other questions and refactored my function in a class that extends the React.Component class but still no luck with iterating. In that case, it just prints nothing to the screen. I've added that code at the bottom as well. Please let me know if I can clear anything up. Thanks in advance!
// This code IS working
// Needed to wrap inner return statement in JSX
const heading = ({info}) => {
console.log(info);
return (
<div>{
info.map(x => {
return (
<div>
<h2>{x.title}</h2>
</div>
)
})
}
</div>
)
}
// Same code without inner return
const heading = ({info}) => {
console.log(info);
return (
<div>
{
info.map(x => (
<div>
<h2>{x.title}</h2>
</div>
)
)
}
</div>
)
}
// prints info in console
const heading = ({info}) => {
console.log(info);
return (
<div>{
info.map(x => {
<div>
<h2>{x.title}</h2>
</div>
})
}
</div>
)
}
const mapState = state => ({ info: state.aboutMe })
const Heading = connect(mapState)(heading);
// EXAMPLE WITH CLASS
// Prints nothing to the screen but doesnt throw error
class homePage extends React.Component {
render() {
const { info } = this.props;
console.log(info);
return (
<div> {
info.map(x => {
<div>
<h2>{x.title}</h2><p>{x.text}</p>
</div>
})
}
</div>
)
}
}
const mapState = state => ({ info: state.aboutMe })
const Heading = connect(mapState)(homePage);
should be
return (
<div>
{info.map(x => (<div>
<h2>{x.title}</h2><p>{x.text}</p>
</div>)
)}
</div>
)
because the div inside the map isn't really being returned
Try explicitly using a return inside map.

Resources