I've read the articles on here on how to do this and have chosen the withRouter(({ history }) => history.push("/")); method, but my code below isn't working.. What am I doing wrong?
import React from "react";
import SearchBox from "./SearchBox";
import { withRouter } from "react-router-dom";
class SearchParams extends React.Component {
handleSearchSubmit() {
withRouter(({ history }) => history.push("/"));
}
render() {
return (
<div className="search-route">
<SearchBox search={this.handleSearchSubmit} />
</div>
);
}
}
export default SearchParams;
withRouter is a higher order component which takes a component as first argument and will make it so that component gets the history added to its regular props.
You can instead use it on the component when you export it, and access the history from this.props.history.
class SearchParams extends React.Component {
handleSearchSubmit = () => {
this.props.history.push("/");
};
render() {
return (
<div className="search-route">
<SearchBox search={this.handleSearchSubmit} />
</div>
);
}
}
export default withRouter(SearchParams);
Related
I have a requirement in which I have to call a function present in a child component from parent component using React.createRef() but the problem here is that it is multilevel child.
I'm able to call a function present in 'ChildComponent1' component but not able to call a function present in 'ChildComponent2' component from 'Parent' component. Please tell me a correct way to do this.
This is my parent.js file
import React, { Component } from 'react';
import ChildComponent1 from './ChildComponent1';
class ParentComponent extends Component {
constructor(props) {
super(props);
this.childRef = React.createRef();
}
callFunctionFromChild = () => {
this.ref.current.doSomething()
}
render() {
return (
<div>
<ChildComponent1 ref={this.childRef} />
<button onClick={()=>{this.callFunctionFromChild()}}> Click Me <button>
</div>
);
}
}
export default ParentComponent;
This is my ChildComponent1.js file:
import React, { Component } from 'react';
import ChildComponent2 from './ChildComponent2';
class ChildComponent1 extends Component {
render() {
return (
<div>
<ChildComponent2 ref={this.props.ref} />
</div>
);
}
}
export default ChildComponent1;
and this is my inner ChildComponent2.js file:
import React, { Component } from 'react';
class ChildComponent2 extends Component {
doSomething = () => {
console.log("Something is done!!");
}
render() {
return (
<div>
My Child Component
</div>
);
}
}
export default ChildComponent2;
Im getting a big headache.. I dont know what Im doing wrong here. When my Podcast.js component renders, I get 'Cannot read property 'params' of undefined... '
Someone that can point me in the right direction?
This is the parent component of Podcast:
import React, { Component } from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import NavLinks from './components/NavLinks';
import Home from './components/Home';
import Podcast from './components/Podcast';
import './App.css';
class App extends Component {
render() {
return (
<Router>
<div className="App">
<NavLinks />
<Route exact path='/' component={Home} />
<Route path='/podcast/:podID' component={Podcast} />
</div>
</Router>
);
}
}
export default App;
This is my main Component (Podcast):
import React, { Component } from 'react';
import PodcastList from './PodcastList';
class Podcast extends Component {
constructor(props) {
super(props);
this.state = {
podcast: []
};
}
// Fetches podID from props.match
fetchPodcast () {
const podID = this.props.match.params.podID
fetch(`https://itunes.apple.com/search?term=podcast&country=${podID}&media=podcast&entity=podcast&limit=20`)
.then(response => response.json())
.then(data => this.setState({ podcast: data.results }));
}
componentDidMount () {
this.fetchPodcast()
}
// Check if new props is not the same as prevProps
componentDidUpdate (prevProps) {
// respond to parameter change
let oldId = prevProps.match.params.podID
let newId = this.props.match.params.podID
if (newId !== oldId)
this.fetchPodcast()
}
render() {
return (
<div>
<PodcastList />
</div>
)
}
}
export default Podcast;
This is the component thats list's all podcasts:
import React, { Component } from 'react';
class PodcastList extends Component {
render() {
return (
<div>
<h2>Country ({this.props.match.params.podID}) </h2>
<ul>
{this.state.podcast.map(podcast =>
<li key={podcast.collectionId}>
<a
href={podcast.collectionId}>
{podcast.collectionName}</a>
</li>
)}
</ul>
</div>
)
}
}
export default PodcastList;
Where does the error comes from? Podcast or PodcastList ? Maybe because you're not passing the props down to PodcastList ?
Try:
<PodcastList {...this.props} {...this.state} />
Also, in the child component (PodcastList) use this.props and not this.state
I guess you are using react-router. To have match prop of the React Router you have to decorate it by withRouter decorator of the module
import React, { Component } from 'react';
import { withRouter } from 'react-router';
class PodcastList extends Component {
render() {
return (
<div>
<h2>Country ({this.props.match.params.podID}) </h2>
<ul>
{this.state.podcast.map(podcast =>
<li key={podcast.collectionId}>
<a
href={podcast.collectionId}>
{podcast.collectionName}</a>
</li>
)}
</ul>
</div>
)
}
}
export default withRouter(PodcastList);
UPDATE:
One of the ways how to handle podcast prop in the PodcastList. The solutions fits all React recommendations and best practices.
import React, { PureComponent } from 'react';
import PodcastItem from './PodcastItem';
class Podcast extends PureComponent { // PureComponent is preferred here instead of Component
constructor(props) {
super(props);
this.state = {
podcast: []
};
}
// Fetches podID from props.match
fetchPodcast () {
const podID = this.props.match.params.podID
fetch(`https://itunes.apple.com/search?term=podcast&country=${podID}&media=podcast&entity=podcast&limit=20`)
.then(response => response.json())
.then(data => this.setState({ podcast: data.results }));
}
componentDidMount () {
this.fetchPodcast()
}
// Check if new props is not the same as prevProps
componentDidUpdate (prevProps) {
// respond to parameter change
let oldId = prevProps.match.params.podID
let newId = this.props.match.params.podID
if (newId !== oldId)
this.fetchPodcast()
}
render() {
return (
<div>
<h2>Country ({this.props.match.params.podID}) </h2>
<ul>
{this.state.podcast.map(podcast => (
<PodcastItem key={podcast.collectionId}> podcast={podcast} />
))}
</ul>
</div>
)
}
}
export default Podcast;
import React from 'react';
// Here Stateless function is enough
const PodcastItem = ({ podcast }) => (
<li key={podcast.collectionId}>
<a href={podcast.collectionId}>{podcast.collectionName}</a>
</li>
);
export default PodcastItem;
I have a FileDrop component, then I use enzyme to test it doesn't render the component correctly and the output is
<Route render={[Function: render]} />
Following is my component:
import React from "react";
import Dropzone from "react-dropzone";
import { withRouter } from "react-router-dom";
import { connect } from "react-redux";
import { dropFiles} from "../../actions/fileActions";
class FileDrop extends React.Component {
constructor(props) {
super(props);
this.onDrop = this.onDrop.bind(this);
}
onDrop(accepted, rejected) {
this.props.dispatch(dropFiles(accepted));
}
getInnerContent(filename) {
return (
<span className="filename-text">
<i className="fa fa-3x fa-files-o" /> {filename ? filename : "Click or drag and drop a CSV file here to upload."}
</span>
);
}
render() {
return (
<Dropzone multiple={false} onDrop={this.onDrop} className="drop" activeClassName="active-drop" rejectClassName="reject-drop" accept=".csv">
<div className="drop-inner">{this.getInnerContent(this.props.droppedFiles.length != 0 ? this.props.droppedFiles[0].name : null)}</div>
</Dropzone>
);
}
}
const mapStateToProps = (state, ownProps) => {
return {
droppedFiles: state.files.droppedFiles
};
};
export default withRouter(connect(mapStateToProps)(FileDrop));
And following is my FileDropSpec.js
import React from 'react';
import { expect } from 'chai';
import { shallow } from 'enzyme';
import FileDrop from '../../../public/scripts/components/fileupload/FileDrop';
describe('<FileDrop/>', function() {
it('should have an input to upload files', function () {
const wrapper = shallow(<FileDrop/>);
console.log(wrapper.debug());
expect(wrapper.find('input')).to.have.length(1);
});
})
This is correct, shallow renders only first lvl of your component tree. And in your case this is withRouter Hoc, for you component test use mount or export file drop component directly.
export class FileDrop extends React.Component {
and use it instead of default export for test with shallow.
http://airbnb.io/enzyme/docs/api/ReactWrapper/mount.html
I have two js files Child.js and App.js.
Child.js
import React from 'react';
const Child = (props) =>{
<div>
<button onClick={props.doWhatever}>{props.title}</button>
</div>
}
export default Child;
App.js
import React, { Component } from 'react';
import './App.css';
import Child from './components/parentTochild/Child.js'
class App extends Component {
state = {
title : 'Helloooo'
}
changeWorld = (newTitle) => {
this.setState = ({
title : newTitle
});
}
render() {
return (
<div className="App">
<Child doWhatever={this.changeWorld.bind(this , 'New world')} title={this.state.title}/>
</div>
);
}
}
export default App;
While executing this code I'm getting the error mentioned in the title. I have tried to solve it. But I couldn't figure out what's the problem with this code.
When I removed <Child doWhatever={this.changeWorld.bind(this , 'New world')} title={this.state.title}/> and typed a text it showed on screen. The problem is when using the Child component.
You should return some thing from child component.
import React from 'react';
const Child = (props) =>{
return (
<div>
<button onClick={(event)=>props.doWhatever('New world')}>{props.title}</button>
</div>
);
}
export default Child;
Updated:
If you want to send a text with the event handler to you can do this :
import React, { Component } from 'react';
import './App.css';
import Child from './components/parentTochild/Child.js'
class App extends Component {
constructor(props){
super(props);
this.state={
title : 'Helloooo'
};
this.changeWorld=this.changeWorld.bind(this);
}
changeWorld = (newTitle) => {
this.setState = ({
title : newTitle
});
}
render() {
return (
<div className="App">
<Child doWhatever={this.changeWorld} title={this.state.title}/>
</div>
);
}
}
export default App;
So, I try to understand how can I make right redirection in my app with event clicks? I put the react-router-dom redirect logic into the button event handler, but it does not work.
What is I'm making wrong?
import React, { Component, Fragment } from 'react';
import Preloader from '../Preloader/Preloader'
import preloaderRunner from '../../Modules/PreloaderRunner'
import { Redirect } from 'react-router-dom';
import axios from 'axios';
class LoginPage extends Component {
constructor(props) {
super(props);
this.state = {
navigate: false
}
}
handleClick = () => {
console.log('Button is cliked!');
return <Redirect to="/employers" />
}
render() {
return (
<Fragment>
<Preloader/>
<h1>This is the Auth Page!</h1>
{this.state.navigate === true
? <div>
<div>You already loggined!</div>
<button onClick={this.handleClick}>Go to the Employers List!</button>
</div>
: <div>
<form>
// some code...
</form>
</div>}
</Fragment>
)
}
}
export default LoginPage;
Things returned by a click handler will not be rendered by your component. You have to introduce a new state property that you can set and then render the <Redirect> component when that property contains a path to redirect to:
class LoginPage extends Component {
constructor(props) {
super(props);
this.state = {
navigate: false,
referrer: null,
};
}
handleClick = () => {
console.log('Button is cliked!');
this.setState({referrer: '/employers'});
}
render() {
const {referrer} = this.state;
if (referrer) return <Redirect to={referrer} />;
// ...
}
}
Alternatively instead of rendering your own button with a click handler you could render a <Link> component as suggested by #alowsarwar that will do the redirect for you when clicked.
I believe on click you want to take the user to '/employers' . Then you need to use Link from the react-router-com. Ideally in React events like 'handleClick' should change the state not return a JSX (this is the wrong approach)
import React, { Component, Fragment } from 'react';
import Preloader from '../Preloader/Preloader'
import preloaderRunner from '../../Modules/PreloaderRunner'
import { Redirect, Link } from 'react-router-dom';
import axios from 'axios';
class LoginPage extends Component {
constructor(props) {
super(props);
this.state = {
navigate: false
}
}
handleClick = () => {
this.setState({ navigate: true});
}
render() {
return (
<Fragment>
<Preloader/>
<h1>This is the Auth Page!</h1>
{this.state.navigate === true
? <div>
<div onClick="this.handleClick">If you want to enable link on some event (Sample test case fyr)</div>
{this.state.navigate ? <Link to='/employers'/> : null}
</div>
: <div>
<form>
// some code...
</form>
</div>}
</Fragment>
)
}
}
export default LoginPage;