React passing argument by path with router and does not render properly - javascript

I have the problem in React, with passing id argument using path in router.
That is my code(index.js):
class App extends React.Component{
render() {
return (
<Router>
<div>
<Home>
<Switch>
<Route path="/mail/:id" component={Mail} />
<Route path="/mail" component={Mail} />
</Switch>
</Home>
</div>
</Router>
)
}}
Next, in Mail.jsx, I have the render function:
render() {
const { activeItem } = this.state;
return (
<Grid>
<Grid.Column width={4}>
<Menu fluid vertical tabular>
<Menu.Item name='mail1' active={activeItem === 'mail1'} onClick={this.handleItemClick}> {this.props.match.params.id}</Menu.Item>
</Menu>
</Grid.Column>
<Grid.Column stretched width={12}>
<div>
<Segment>
{this.state.mails.length > 0 && this.state.mails ? this.state.mails[0].name : null}
</Segment>
</div>
</Grid.Column>
</Grid>
)
}
The problem is, that when I write http://localhost:3000/mail I see the "this.state.mails[0].name" and no id ofcourse because I didnt pass one, but when I write http://localhost:3000/mail/5 I can see the number five on the page, but no "this.state.mails[0].name" anymore, why is that?
And getting data from json file:
componentDidMount(){
axios.get("db.json")
.then( (response) => {
console.log(response);
this.setState({
mails: response.data.mail,
total: response.data.mail.length
});
})
.catch(function (error) {
console.log('error');
console.log(error);
});
}
Edit, that is my render function in Home.jsx:
render() {
const { activeItem } = this.state;
return(
<div>
<div>
<MenuExampleSecondaryPointing/>
</div>
<div>
{this.props.children}
</div>
</div>
);
}

I found an answer from a friend, that is that simple as that:
" axios.get("db.json")" should be "axios.get("/db.json")"
because with "/" I search in all catalogs for that file

Related

React.js - Functions are not valid as a React child

I am new to React.js. I can't solve the problem. I am getting this warning:
Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.
App.js
`
import React from 'react';
import MovieList from './MovieList';
import SearchBar from './SearchBar';
import AddMovie from './AddMovie';
import axios from 'axios'
import { BrowserRouter as Router, Routes, Route } from "react-router-dom"
class App extends React.Component {
state = {
movies: [],
searchQuery: ""
}
async componentDidMount() {
const response = await axios.get("http://localhost:3002/movies")
this.setState({movies: response.data})
}
deleteMovie = async (movie) => {
axios.delete(`http://localhost:3002/movies/${movie.id}`)
const newMovieList = this.state.movies.filter(
m => m.id !== movie.id
)
this.setState(state => ({
movies: newMovieList
}))
}
searchMovie = (event) => {
this.setState({searchQuery: event.target.value })
}
render() {
let filteredMovies = this.state.movies.filter(
(movie) => {
return movie.name.toLowerCase().indexOf(this.state.searchQuery.toLowerCase()) !== -1
}
)
return (
<Router>
<div className="container">
<Routes>
<Route path='/' exact element={() =>(
<React.Fragment>
<div className="row">
<div className="col-lg-12">
<SearchBar const searchMovieProp={this.searchMovie()} />
</div>
</div>
<MovieList
movies={filteredMovies()}
deleteMovieProp={this.deleteMovie()}
/>
</React.Fragment>
)}>
</Route>
<Route path='/add' element={<AddMovie />} />
</Routes>
</div>
</Router>
)
}
}
export default App;
`
What am I doing wrong?
Thanks in advance.
Passing a function to a route like you did:
<Route path='/' exact element={() =>(
<React.Fragment>
<div className="row">
<div className="col-lg-12">
<SearchBar const searchMovieProp={this.searchMovie()} />
</div>
</div>
<MovieList
movies={filteredMovies()}
deleteMovieProp={this.deleteMovie()}
/>
</React.Fragment>
)}>
looks like a router v5 syntax. This is not working in v6: you should pass an element, which is different than a function producing an element. Something like this would work:
<Route path='/' exact element={(
<React.Fragment>
<div className="row">
<div className="col-lg-12">
<SearchBar const searchMovieProp={this.searchMovie()} />
</div>
</div>
<MovieList
movies={filteredMovies()}
deleteMovieProp={this.deleteMovie()}
/>
</React.Fragment>
)}>

React Class Component is not changing with the change of its props [duplicate]

react-router-dom v5 and React 16
My loading app component contains:
ReactDOM.render(
<FirebaseContext.Provider value={new Firebase()}>
<BrowserRouter>
<StartApp />
</BrowserRouter>,
</FirebaseContext.Provider>,
document.getElementById("root")
);
I have a route component which contains:
{
path: "/member/:memberId",
component: MemberForm,
layout: "/admin"
},
Admin component:
return (
<>
<div className="main-content" ref="mainContent">
<LoadingComponent loading={this.props.authState.loading}>
<AdminNavbar
{...this.props}
brandText={this.getBrandText(this.props.location.pathname)}
/>
<AuthDetailsProvider>
<Switch>{this.getRoutes(routes)}</Switch>
</AuthDetailsProvider>
<Container fluid>
<AdminFooter />
</Container>
</LoadingComponent>
</div>
</>
)
this.getRoutes in the Switch contains the reference route above.
Now from one of my component pages I can navigate to /member/{memberid} this works fine.
the route loads a component called MemberForm
inside MemberForm I have a row that contains this method:
<Row>
{ this.displayHouseholdMembers() }
</Row>
displayHouseholdMembers = () => {
const householdDetails = this.state.family;
if (householdDetails) {
return householdDetails.map((key, ind) => {
if (key['uid'] != this.state.memberKeyID) {
return (
<Row key={ind} style={{ paddingLeft: '25px', width: '50%'}}>
<Col xs="5">
<Link to={ key['uid'] }>
{ key['first'] + " " + key['last'] }
</Link>
</Col>
<Col xs="4">
{ key['relation'] }
</Col>
<Col xs="3">
<Button
color="primary"
size="sm"
onClick={(e) => this.removeHouseRelation(key)}
>
Remove
</Button>
</Col>
</Row>
);
}
});
}
};
MemberForm:
in componentDidMount I do an firebase call to check for the data pertaining to the user using the uid aka memberId in the URL.
class MemberForm extends React.Component {
constructor(props) {
super(props);
this.state = {
...INITIAL_STATE,
currentOrganization: this.props.orgID,
householdRelation: ['Spouse', 'Child', 'Parent', 'Sibling'],
householdSelected: false,
};
}
componentDidMount() {
let urlPath, personId;
urlPath = "members";
personId = this.props.match.params.memberId;
// if it is a member set to active
this.setState({ statusSelected: "Active" })
this.setState({ memberSaved: true, indiUid: personId });
// this sets visitor date for db
const setVisitorDate = this.readableHumanDate(new Date());
this.setState({ formType: urlPath, visitorDate: setVisitorDate }, () => {
if (personId) {
this.setState({ memberSaved: true, indiUid: personId });
this.getIndividualMemberInDB(
this.state.currentOrganization,
personId,
this.state.formType,
INITIAL_STATE
);
}
});
}
...
return (
<>
<UserHeader first={s.first} last={s.last} />
{/* Page content */}
<Container className="mt--7" fluid>
<Row>
...
<Row>
{ this.displayHouseholdMembers() }
</Row>
</Form>
</CardBody>
) : null}
</Card>
</Col>
</Row>
<Row>
<Col lg="12" style={{ padding: "20px" }}>
<Button
color="primary"
onClick={e => this.submitMember(e)}
size="md"
>
Save Profile
</Button>
{ this.state.indiUid ? (
<Button
color="secondary"
onClick={e => this.disableProfile()}
size="md"
>
Disable Profile
</Button>
) : null }
</Col>
</Row>
</Container>
</>
);
When I click on the Link it shows the url has changed 'members/{new UID appears here}' but the page does not reload. I believe what's going on is that since it's using the same route in essence: path: "/member/:memberId"it doesn't reload the page. How can I get it to go to the same route but with the different memberId?
You are correct that the MemberForm component remains mounted by the router/route when only the path param is updating. Because of this the MailForm component needs to handle prop values changing and re-run any logic depending on the prop value. The componentDidUpdate is the lifecycle method to be used for this.
Abstract the logic into a utility function that can be called from both componentDidMount and componentDidUpdate.
Example:
getData = () => {
const urlPath = "members";
const { memberId } = this.props.match.params;
// this sets visitor date for db
const setVisitorDate = this.readableHumanDate(new Date());
this.setState(
{
// if it is a member set to active
statusSelected: "Active",
memberSaved: true,
indiUid: memberId,
formType: urlPath,
visitorDate: setVisitorDate
},
() => {
if (memberId) {
this.setState({ memberSaved: true, indiUid: memberId });
this.getIndividualMemberInDB(
this.state.currentOrganization,
memberId,
this.state.formType,
INITIAL_STATE
);
}
}
);
}
The lifecycle methods:
componentDidMount() {
this.getData();
}
componentDidUpdate(prevProps) {
if (prevProps.match.params.memberId !== this.props.match.params.memberId) {
this.getData();
}
}
For react-router-dom v6, can you try with simple routing? Create a Test.js with
const Test = ()=> <h1>Test Page</h1>
Then, create a Home.js with
const Home = ()=> <Link to="/test">Test</Link>
Then, add them to route.
<BrowserRouter>
<Routes>
<Route path="/" element={<Home/>} />
<Route path="/test" element={<Test />} />
</Routes>
</BrowserRouter>
Does your component structure look like this? For index route, look more at https://reactrouter.com/docs/en/v6/getting-started/overview.

React won't map over an array of objects, TypeError: data.map is not a function

I am following Brad Traversy's React front to back course and I have requested from the github API with axios in app.js
This is the initial state of App.js
class App extends Component {
state = {
users: [],
user: {},
loading: false,
alert: null,
};
This is the API call in App.js
searchUsers = async (text) => {
this.setState({ loading: true });
console.log(text);
const res = await axios.get(
`https://api.github.com/search/users?q=${text}&client_id=${process.env.REACT_APP_GITHUB_CLIENT_ID}&client_secret=${process.env.REACT_APP_GITHUB_CLIENT_SECRET}`
);
this.setState({ users: res.data.items, loading: false });
console.log(res.data.items);
};
Then my Users component takes the users from the state but here is my entire main app.js root render()
render() {
// destrcuturing so no need for this.state in Users component when passing props
const { users, loading, alert, user } = this.state;
return (
<Router>
<div className='App'>
<Navbar />
<div className='container'>
<Alert alert={alert} />
<Switch>
<Route
exact
path='/'
render={(props) => (
<Fragment>
<Search
searchUsers={this.searchUsers}
clearUsers={this.clearUsers}
showClear={users.length > 0 ? true : false}
setAlert={this.setAlert}
/>
<Users loading={loading} users={users} />
</Fragment>
)}
/>
<Route exact path='/about' component={About} />
<Route
exact
path='/user/:login'
render={(props) => (
<User
{...props}
getUser={this.getUser}
user={user}
loading={loading}
/>
)}
/>
</Switch>
</div>
</div>
</Router>
);
}
I console.log the users data in the searchUsers method and in the Users component where I am mapping over this array. The output is clearly an array of objects https://imgur.com/a/JucrNYh
When I load my app from the homepage and search a name it works and renders/maps over the users I can click on the More button for an individual user. However, when I go back using the back to search button in the navbar I get the error that users.map is not a function. I am running node version 14.5.0
Here is Users.js
const Users = ({ loading, users }) => {
console.log('users are a ' + typeof users + ' it looks like below');
console.log(users);
if (loading) {
return <Spinner />;
} else {
return (
<div style={userStyle}>
{users.map((user) => (
<UserItem user={user} key={user.id} />
))}
</div>
);
}
};
and my Navbar.js
const Navbar = ({ icon, title }) => {
return (
<nav className='navbar bg-primary'>
<h1>
<i className={icon}></i>
{title}
</h1>
<ul>
<li>
<Link to='/'>Home</Link>
</li>
</ul>
<ul>
<li>
<Link to='/about'>About</Link>
</li>
</ul>
</nav>
);
};
Here is my User component which is what is rendered when the more button is clicked in UserItem.js
export class User extends Component {
componentDidMount() {
this.props.getUser(this.props.match.params.login);
}
static propTypes = {
loading: PropTypes.bool.isRequired,
user: PropTypes.object.isRequired,
getUser: PropTypes.func.isRequired,
};
render() {
const {
name,
avatar_url,
location,
bio,
blog,
login,
html_url,
followers,
following,
public_repos,
public_gists,
hirable,
} = this.props.user;
const { loading } = this.props;
if (loading) return <Spinner />;
return (
<Fragment>
<Link to='/' className='btn btn-light'>
Back to search
</Link>
Hireable: {''}
{/* {hireable ? (
<i className='fas fa-check text-success' />
) : (
<i className='fas fa-times-circle text-danger' />
)} */}
</Fragment>
);
}
}
The error is reproduced on different steps sometimes when I click on the more button on a user the User.js component does not render however the Navbar is there and no errors are produced but I get users.map is not a function when I click the Navbar home. Other times like I said above the User.js does render and I get the same error whether I click home in the Navbar or the back to search button.

Routing not rendering component

Below code displays a list of items.
App.js
render() {
return (
<Router>
<div className="App">
<AppContext.Provider value={{ state: this.state }}>
<ItemList />
<Switch>
<Route path="/detail/:id" component={ItemDetail} />
<Route exact={true} path="/" component={ItemList} />
</Switch>
</AppContext.Provider>
</div>
</Router>
);
}
ItemList.js
render() {
return (
<AppContext.Consumer>
{context => {
return context.state.itemList.map(item => {
return (
<Link key={item.id} to={`/detail/${item.id}`}>
<p>{item.id}</p>
</Link>
);
});
}}
</AppContext.Consumer>
);
}
ItemDetail.js
import React from "react";
export default function ItemDetail(props) {
return <div>Item detail</div>;
}
on clicking any item from the list, item detail should be displayed (from ItemDetail component). problem is only url is getting changed to /detail/id, but component is not getting changed ie not changing to ItemDetail.js

Material-UI BottomNavigationItem URL

UI on a react component. I have a <BottomNavigationItem /> component. This actually renders as an <button>. How can I actually make it render/navigate to a URL?
class FooterNavigation extends Component {
state = {
selectedIndex: 0,
};
select = (index) => this.setState({selectedIndex: index});
render() {
return (
<footer className="mdl-mini-footer">
<Paper zDepth={1}>
<BottomNavigation selectedIndex={this.state.selectedIndex}>
<BottomNavigationItem
label="Reviews"
icon={reviewIcon}
onClick={() => this.select(0)}
/>
</BottomNavigation>
</Paper>
</footer>
);
}
}
Simply you can just add containerElement={<Link to="/home"/>} don't forget to import Link from react-router-dom
So it will be like this:
<BottomNavigationItem
containerElement={<Link to="/home"/>}
label="Reviews"
icon={reviewIcon}
onClick={() => this.select(0)}
/>

Categories