Reactjs displays error users.map is not a function - reactjs

I am getting the following error in my React application:
users.map is not a function
I have tried many solutions posted on Stack Overflow but it does not seems to solve my issue.
I used the code below to submit a filename and it works fine. Here is my problem. Each time i click on submit button, i
want to display a JSON data from the backend in a succession (For Instance If I submit form 3 times, I need to have 3 records of JSON data showed).
Here is the sample of JSON:
{"filename":"macofile","message":"success","uid":"20"}
To this effect I have set the following line of code in the Axios Post response
this.setState({
users: res.data,
loading: false,
});
I have also tried
users: res
or
users.push(res.data);
This is my code:
import React, { Component } from "react";
import axios, { post } from "axios";
class FilePage extends React.Component {
constructor(props) {
super(props);
this.state = {
value: "",
filename: "",
loading: false,
users: [],
error: null
};
this.handleChange = this.handleChange.bind(this);
}
_handleSubmit(e) {
e.preventDefault();
//send it as form data
const formData = new FormData();
formData.append("filename", this.state.filename);
//alert(this.state.filename);
this.setState({ loading: true }, () => {
axios
.post("http://localhost/apidb_react/up.php", formData)
.then(res => {
this.setState({
users: res.data,
loading: false
});
})
.catch(err => {
console.log(err.message);
});
});
}
// handle form submission
handleChange(event) {
this.setState({ [event.target.name]: event.target.value });
}
render() {
const { loading, users, error } = this.state;
return (
<div>
<form onSubmit={e => this._handleSubmit(e)}>
<b>filename:</b>
<input
tyle="text"
className="form-control"
value={this.state.filename}
name="filename"
onChange={this.handleChange}
/>
<button
className="submitButton"
type="submit"
onClick={e => this._handleSubmit(e)}
>
submit
</button>
</form>
<React.Fragment>
<h3>Display Data each time record is submitted</h3>
{error ? <p>{error.message}</p> : null}
{!loading ? (
users.map(user => {
const { filename, message, uid } = user;
return (
<div key={uid}>
<p>Userid: {uid}</p>
<p>File Name: {filename}</p>
<p>Message: {message}</p>
<hr />
</div>
);
})
) : (
<h3>Loading...</h3>
)}
</React.Fragment>
</div>
);
}
}

Your res.data seems to be an object rather than an array -> {"filename":"macofile","message":"success","uid":"20"}. So, you will need to loop through the object by taking an array for eg:
Object.keys(users).map(key => console.log(users[key]))

Related

React - show loading message before fetched data is rendered

I'm trying to build a react app that communicating with the server to query data from database. I want to display a loading message when the app is fetching data. Here's the code snippet:
class App extends React.Component {
constructor(props) {
super(props)
this.clickHandler = this.clickHandler.bind(this);
this.state = {
isLoading: false,
data: [],
content: ''
}
}
clickHandler() {
this.setState({ isLoading: true });
fetch('url_1')
.then(res => res.json())
.then(data => this.setState({ data: data }, () => {
this.getContentByData() // loading message vanished before content is displayed
}))
.then(() => this.setState({ isLoading: false }));
}
getContentByData() {
fetch(`url_2?data=${this.state.data}`)
.then(res => res.json())
.then(content => this.setState({ content: content }))
}
render() {
return (
<div>
{this.state.isLoading ? <h1>loading</h1>: null}
<h6>{this.state.content}</h6>
<button type="button" onClick={this.clickHandler}>Click Me!</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
The loading message disappeared before content is displayed. How can I fix this?
You could change your render method to be something like this:
render() {
let content = <Loader />;
if (!this.state.isLoading && this.state.content) {
content = (
<h6>{this.state.content}</h6>
<button type="button" onClick={this.clickHandler}>Click Me!</button>
)
}
return (
<div>
{content}
</div>
)
}
Where Loader is your loader component. Of course it can also be the h1 title or whatever you want.
you need to off your loading at getContentByData() like below.
getContentByData() {
fetch(`url_2?data=${this.state.data}`)
.then(res => res.json())
.then(content => this.setState({ content: content, isLoading : false }))
}
do not forget to remove isLoading=false in clickHandler() method.
isLoading will be set to false before the fetch inside getContentByData is complete, since it's not part of the promise chain in clickHandler.
You could instead set isLoading to false when the fetch in getContentByData is complete.
class App extends React.Component {
state = {
isLoading: false,
data: [],
content: ""
};
clickHandler = () => {
this.setState({ isLoading: true });
fetch("url_1")
.then(res => res.json())
.then(data => this.setState({ data }, this.getContentByData));
};
getContentByData = () => {
fetch(`url_2?data=${this.state.data}`)
.then(res => res.json())
.then(content => this.setState({ isLoading: false, content }));
}
// ...
}
Or even better you can render something like this:
render() {
return (
<div>
{this.state.isLoading && !this.state.content && <Loader />}
{!this.state.isLoading && this.state.content && (
<Fragment>
<h6>{this.state.content}</h6>
<button type="button" onClick={this.clickHandler}>Click Me!</button>}
</Fragment>)
</div>
)
}

Reactjs Event/Action Button not switching as expected

Reactjs Event/Action Button not switching as expected.
Am trying to add follow and unfollow action button. when I post via axios via Follow button,
it post to data to server backend and return success message. Then the Follow button switched to Unfollow button.
Now my problem is that Unfollow button is not switching back to Follow Button when User try to unfollow someone.
Please what am I doing wrong here.
here is the json success message
[{"status":"success", "follow":"1", "unfollow":"0"}]
here is the my code
import React, { Component, Fragment } from "react";
import { render } from "react-dom";
import axios from 'axios';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
result_data: '',
data: [],
loading: false
};
}
componentDidMount() {
this.setState({
data: [{"uid":"1","name":"Nancy"},{"uid":"2","name":"Moore"}],
});
}
// update user following
handleFollowUser(user_id) {
const uid_data = { user_id: user_id };
axios
.get("http://localhost/data.json", { uid_data })
.then(response => {
this.setState(state => ({
//data: newData,
result_data: response.data[0].status
}));
alert(result_data);
})
.catch(error => {
console.log(error);
});
}
// update user unfollowing
handleUnFollowUser(user_id) {
const uid_data = { user_id: user_id };
axios
.get("http://localhost/data.json", { uid_data })
.then(response => {
this.setState(state => ({
//data: newData,
result_data: response.data[0].status
}));
alert(result_data);
})
.catch(error => {
console.log(error);
});
}
render() {
return (
<span>
<label>
<ul>
<h1>Users</h1> <br />
{this.state.result_data }
{this.state.data.map((users) => {
return (
<div key={users.uid}>
<div>
<b> Name: </b>{users.name}
<br />
{this.state.result_data === ''
? <span onClick={() =>
this.handleFollowUser(users.uid)}>Follow</span>
: <span onClick={() =>
this.handleUnFollowUser(users.uid)}>unfollow</span>
}
</div>
</div>
)
}
)}
</ul>
</label>
</span>
);
}
}
This is what solved my Reactjs problem.I first initialize
isToggleOn: !state.isToggleOn in the click event and in the constructor I implemented
this.state = {isToggleOn: true};
My click button becomes
<button onClick={this.handleFollowUser}>
{this.state.isToggleOn ? 'Follow' : 'Unfollow'}
</button>

redirect to different route if input was submitted - React Router

I am creating a React Application for searching for movies using the OMDb.
My app works in two stages:
You are presented initially with a search box
Upon submitting the search box with a movie query, the app should redirect to a results page.
Below is my Main application:
class Muuvies extends Component {
constructor() {
super();
this.state ={
movies: [],
error: "",
searchedFor: "",
};
this.search = this.search.bind(this);
}
search({data}) {
fetch(`http://www.omdbapi.com/?apikey=${OMDB_API_KEY}&s=${data}&type=movie`)
.then(res => res.json())
.then(res => {
return res;
}).then(json => this.setState({
searchedFor: data,
movies: propOr([], 'Search', json),
error: json.Error
})).catch(err => this.setState({
error: 'Error Occurred: Try Again',
movies: [],
searchedFor: ''
}));
// if there wasn't an error, redirect to the Results page, passing what is returned from fetch().
}
render() {
return (
<Container>
<Logo role="img" aria-label="search">🍿</Logo>
<SearchWrapper>
<Search search={this.search} />
{
this.state.error
? <Error><span role="img" aria-label="error" style={{marginRight: 2 + 'px'}}>⚠️</span>{this.state.error}</Error>
: null
}
</SearchWrapper>
</Container>
);
}
}
How do I change my application so, that if the user submits the form and there isn't an error (e.g. this.state.error is empty after the fetch()), then it redirects them to the Results component?
Contents of Search.js:
class Search extends Component {
constructor(props) {
super(props);
this.state = {
data: '',
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({[e.target.name]: e.target.value});
}
handleSubmit(e) {
e.preventDefault();
this.props.search(this.state);
this.setState({ data: '' });
}
render () {
return (
<StyledForm onSubmit={this.handleSubmit}>
<StyledSearch id="data" type="text"
name="data"
placeholder="Search for Muuvies."
value={path(['state', 'data'], this)}
onChange={this.handleChange}
autoComplete="off"
autoFocus={true}
/>
</StyledForm>
)
}
}
Given you are using react-router-dom you can use history's push() method. This would need to be used with wrapping component withRouter to expose the necessary props. This would also need to be done with then() do to the async nature of your API call/search:
import { withRouter } from 'react-router-dom';
class Muuvies extends Component {
constructor() {
super();
this.state ={
movies: [],
error: "",
searchedFor: "",
};
this.search = this.search.bind(this);
}
search({data}) {
fetch(`http://www.omdbapi.com/?apikey=${OMDB_API_KEY}&s=${data}&type=movie`)
.then(res => res.json())
.then(res => {
return res;
}).then(json => {
// you may not need this if your are redirecting
this.setState({
searchedFor: data,
movies: propOr([], 'Search', json),
error: json.Error
}, () => {
this.props.history.push("/results"); // or whatever string path
});
}).catch(err => this.setState({
error: 'Error Occurred: Try Again',
movies: [],
searchedFor: ''
}));
// this would execute before API call/search is complete, need to do it in then()
}
render() {
return (
<Container>
<Logo role="img" aria-label="search">🍿</Logo>
<SearchWrapper>
<Search search={this.search} />
{
this.state.error
? <Error><span role="img" aria-label="error" style={{marginRight: 2 + 'px'}}>⚠️</span>{this.state.error}</Error>
: null
}
</SearchWrapper>
</Container>
);
}
}
export default withRouter(Muuvies);
This is assuming you have a Route defined for your Results component/path:
import Results from './path/to/Results';
// ...
<Route path="/results" component={Results} />
You could also use the Redirect component in render() method, perhaps based on some state values:
import { Redirect } from 'react-router-dom';
// ...
render() {
// check for error and maybe some other flag(s) such as a boolean property
if (!this.state.error && this.state.submitted && this.state.movies.length > 0) {
return <Redirect to={{ pathname: '/results', state: { movies: this.state.movies } }} />
}
// else render form or error maybe
}
That being demonstrated, if you are planning to navigate at the component's level to "/results" or whatever the path may be, You probably wouldn't need to set state. You can pass state as a second argument to the history.push(path, state) method if necessary.
Hopefully that helps!
You can write your code here:
search({data}) {
fetch(`http://www.omdbapi.com/?apikey=${OMDB_API_KEY}&s=${data}&type=movie`)
.then(res => res.json())
.then(res => {
return res;
}).then(json => {
this.setState({
searchedFor: data,
movies: propOr([], 'Search', json),
error: json.Error
});
//------
//Write your code here, because if you got there, then there was no error at none of the "then" above
//Or if a success response can contain an error field, you can check it with an if statement
//------
}).catch(err => this.setState({
error: 'Error Occurred: Try Again',
movies: [],
searchedFor: ''
}));
The code depends of how are you structurating your project and which libraries are you using.

Correct way to React axios post with UUID and nested object in JSON payload?

My server-side application accepts an int, does some simple math, and returns an int as Content-Type application/json. The api has been tested with Postman and works correctly.
I'm looking for the proper way to handle an Axios POST with a JSON payload that includes a UUID with an object nested below it. As suggested, I added [''] around the UUID to play nicely with React. If I click 'Post' without entering a value my server returns an int for 'current_value'. If I enter a number in the field 'current_value' returns a string e.g., 4 + 2 = "42".
import React, { Component } from 'react';
import axios from 'axios';
class Post extends Component {
constructor() {
super();
this.state = {
current_value: 0
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange = event => {
this.setState({ current_value: event.target.value });
console.log(event.target.value);
};
handleSubmit = event => {
event.preventDefault();
axios.post('http://my.server.url', {
foo: 'bar',
['e0ea641b-3de4-4a76-857d-11da9352698a']: { current_value: this.state.current_value }
})
.then(response => {
this.setState({ current_value: response.data.current_value });
console.log(JSON.stringify(response.data));
});
};
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label>Input Number:
<input type="number" name="current_value" onChange={this.handleChange} />
</label>
<button type="submit">Post</button>
</form>
<div>
Output Number: { this.state.current_value }
</div>
</div>
);
}
}
export default Post;
Try to escape your uuid like below, it should work:
{
foo: 'bar',
['e0ea641b-3de4-4a76-857d-11da9352698a']:{ current_value: this.state.current_value }
}
With a nod to help from #GuilhermeLemmi, I've landed on the answer that addresses both my initial issue and the problem of handling the response where the item in question contains a minus sign -. Wrapping my UUID in [] in the data object wasn't necessary, but I did need to wrap it in single quotes. On the return side I did need to wrap the response in [''] but leave it as an object, don't JSON.stringify() it. Now everything flows nice and smooth.
import React, { Component } from 'react';
import axios from 'axios';
class Post extends Component {
constructor() {
super();
this.state = {
current_value: 0
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange = event => {
this.setState({ current_value: JSON.parse(event.target.value)});
console.log(event.target.value);
};
handleSubmit = event => {
event.preventDefault();
const data = {
foo: 'bar',
'e0ea641b-3de4-4a76-857d-11da9352698a': {
current_value: this.state.current_value
}
};
console.log(data);
axios.post('http://my.server.url', data)
.then(response => {
const obj = response.data;
this.setState({ current_value: obj['e0ea641b-3de4-4a76-857d-11da9352698a'].current_value });
console.log(obj['e0ea641b-3de4-4a76-857d-11da9352698a'].current_value);
})
.catch((error) => {
console.log(error);
});
};
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label>Input Number:
<input type="number" name="current_value" onChange={this.handleChange} />
</label>
<button type="submit">Post</button>
</form>
<div>
Output Number: { this.state.current_value }
</div>
</div>
);
}
}
export default Post;

Update Apollo store after filtered query

I have a list of items which can be filtered out using certain criteria. Whenever i perform a search, i want to grab a filtered item and update its content which seems to update the apollo store correctly, but for some reason changes are not being shown. Perhaps the componentWillReceiveProps lifecycle method is not being fired and i need to implement it by myself?
I tried updating the store manually using "update" after the mutation but it wont work also.
This is my code so far:
ClientList.js
class ClientList extends Component {
constructor(props) {
super(props);
this.state = {
hasMoreItems: true,
loading: false,
clients: [],
searchText: ''
}
}
_executeSearch = async () => {
const { searchText } = this.state;
this.setState({ loading: true });
const result = await this.props.client.query({
query: ALL_CLIENTS_QUERY,
variables: { searchText },
fetchPolicy: 'network-only'
})
this.setState({
clients: result.data.allClients,
loading: false
});
}
render() {
let { allClients, loading, fetchMore } = this.props.data;
const { hasMoreItems, clients, searchText } = this.state;
if (clients.length > 0) {
allClients = clients;
loading = this.state.loading;
}
return (
<section>
<h1 className="text-center">Clients</h1>
<InputGroup>
<InputGroupButton>
<Button onClick={() => this._executeSearch()}>I'm a button</Button>
</InputGroupButton>
<Input
onChange={(e) => this.setState({ searchText: e.target.value })}
placeholder="Search by social or comercial name"
/>
</InputGroup>
{loading ?
<div className="text-center mt-4">
<i className="fa fa-circle-o-notch fa-spin fa-3x fa-fw"></i>
</div>
: <div className="mt-3">
{allClients.map(client =>
<div key={`client-${client.id}`} className="client-content">
<Link to={`/clients/${client.id}`}>
<h1 className="mb-1">
{client.socialName}
<small className="text-muted ml-3">{client.comercialName}</small>
</h1>
</Link>
</div>
})
</div>
</section>
);
};
}
export default withApollo(graphql(ALL_CLIENTS_QUERY)(ClientList));
ClientEdit.js
class ClientEdit extends Component {
onSubmit = async (e) => {
e.preventDefault();
this.setState({ loading: true });
const payload = {
id: this.props.match.params.id,
rfc: this.state.rfc,
socialName: this.state.socialName,
legalRepresentative: this.state.legalRepresentative,
comercialName: this.state.comercialName
}
// Mutation updates the store but doesnt show results
const resp = await this.props.mutate({
variables: payload,
update: (store, { data: { updateClient } }) => {
// Tried updating but it doesnt show changes also;
}
});
}
}
export default compose(
graphql(GET_CLIENT_QUERY, {
options: props => ({
variables: {
id: props.match.params.id
}
})
}),
graphql(UPDATE_CLIENT_MUTATION)
)(ClientEdit);
better if we check that the data is ready and we can render it out , and when data still fetching so no render associated with that apollo data object should be done:
render(){
const {loading} = this.props.data;
if(loading) {return <div>Loading...</div>}
return (
...
)
I manage to fix this using the componentWillReceiveProps lifecycle method. I dont know if this is a bug or maybe there is another solution;
componentWillReceiveProps(newProps) {
const { allClients, loading } = newProps.data;
if (!loading) {
const clients = _.intersectionBy(allClients, this.state.clients, 'id');
this.setState({ clients });
}
}

Resources