Checking props of a child component with shallow rendering using enzyme - reactjs

I have a problem understanding shallow rendering of enzyme.
I have a component WeatherApplication which has a child component CitySelection.
The CitySelection receives a property selectedCity which is hold in the WeatherApplications state.
The component:
export default class WeatherApplication extends React.Component {
constructor(props) {
super(props);
this.state = {
city : "Hamburg"
}
}
selectCity(value) {
this.setState({
city: value
});
}
render() {
return (
<div>
<CitySelection selectCity={this.selectCity.bind(this)} selectedCity={this.state.city} />
</div>
);
}
}
I tested sussessfully that the CitySeleciton exists and that the selectedCity is "Hamburg" and the correct function is passed.
Now I want to test the behaviour of the selectCity method.
it("updates the temperature when the city is changed", () => {
var wrapper = shallow(<WeatherApplication/>);
wrapper.instance().selectCity("Bremen");
var citySelection = wrapper.find(CitySelection);
expect(citySelection.props().selectedCity).toEqual("Bremen");
});
This test fails, because the value of citySelection.props().selectedCity is still Hamburg.
I checked that the render method of WeatherApplication is called again and this.state.city has the correct value. But I cannot fetch it via the props.

Calling wrapper.update() after selectCity() should do the trick:
it("updates the temperature when the city is changed", () => {
var wrapper = shallow(<WeatherApplication/>);
wrapper.instance().selectCity("Bremen");
wrapper.update();
var citySelection = wrapper.find(CitySelection);
expect(citySelection.props().selectedCity).toEqual("Bremen");
});

Related

How can I correctly pass state as props from one component to another?

I'm trying to pass my state as props from component Locatione.js to Map.js, so the props are available when I call the function SendLocation in Map.js.
Here is my component Locatione
export default class Locatione extends Component {
state = {
location: null
};
componentDidMount() {
this._getLocationAsync();
}
_getLocationAsync = async () => {
let location = await Location.getCurrentPositionAsync({ });
this.setState({ location });
console.log("log this pls", this.state); // the state here logs correctly
};
render() {
return (
<Map locatione={this.state} /> // when accesing this props in Map, I'm getting **null**
);
}
}
Here is my Map.js component
export default class Map extends React.Component {
sendLocation() {
console.log("sending location log", this.props); // the props here appear as null
}
render() {
return (
<Button
title="Send Sonar"
onPress={(this.sendLocation, () => console.log("hi", this.props))} //the props here log correctly
/>
);
}
}
I also tried passing my props in this fashion, to no avail.
export default class Map extends React.Component {
sendLocation(altitude, longitude) {
console.log("sending location log", this.props);
}
render() {
return (
<Button
title="Send Sonar"
onPress={(this.sendLocation, (this.props)))}
/>
);
}
}
Thanks for your help
There is a little problem here:
onPress={(this.sendLocation, () => console.log("hi", this.props))}
The console.log will trigger everytime the code renders or re-renders the button, not when you click it.
If you want to log after you call a function change the onPress to:
onPress={() => {
this.sendLocation()
console.log("hi", this.props)
}}
The other problem is that you are not giving your sendLocation function access to this.
You have two ways of doing it:
First way: Binding it inside your constructor. So inside your Map.js you add it like:
constructor(props){
super(props);
this.sendLocation.bind(this);
}
Second way: Declaring your sendLocation function as an arrow function:
sendLocation = () => {
console.log("sending location log", this.props);
}
Just as you can pass regular values as props, you can also grab data from a component’s state and pass it down as props for any of its child components. You just need to pass the exact value, also use constructor in case of class components.
`export default class Location extends Component {
constructor(props) {
super(props);
this.state = {
location: null
};
}
render() {
return (
<Map location={this.state.location} />
);
}
}`
You need to pass the function to onPress and use arrow function to be able to use this inside sendLocation.
class Map extends React.Component {
sendLocation = () => {
console.log('sending location log', this.props.locatione); // the props here appear as null
};
render() {
return (
<Button
title="Send Sonar"
onPress={this.sendLocation}
/>
);
}
}
You are passing the props through components correctly, but you should use arrow function and also anonymous func.
Try:
export default class Map extends React.Component {
sendLocation = (altitude, longitude) => {
console.log("sending location log", this.props);
}
render() {
return (
<Button
title="Send Sonar"
onPress={()=>this.sendLocation}
/>
);
}
}

React: How to access the props object outside its class?

I am not sure how to access the props object outside the context of a receiving child, is there any way? Here is a minimal test file (Jest and Enzyme) I setup to experiment with. I get a syntax error where I try to pass this.props.propNumber in:"this is a reserved word"
const React = require("react");
const enzyme = require("enzyme");
const Adapter = require("enzyme-adapter-react-16");
enzyme.configure({ adapter : new Adapter() });
// simple component with an increment method
class Foo extends React.Component {
constructor(props) {
super(props);
this.state = {
testIncrement : 1
};
}
handleIncrement = (incrementSize) => {
this.setState({
testIncrement : this.state.testIncrement += incrementSize
})
};
render() {
return (
<div>
Test
</div>
);
}
}
const wrapper = enzyme.shallow(
<Foo
propNumber={5}
/>
);
test('prop passage into shallow instance', () => {
wrapper.instance().handleIncrement(this.props.propNumber);
expect(wrapper.state('testIncrement')).toEqual(6);
});
You know what the prop is because you are passing it in, why do you need to access it from the class?
const incrementSize = 1;
const propNumber = 5;
const wrapper = enzyme.shallow(
<Foo
propNumber={propNumber}
/>
);
test('prop passage into shallow instance', () => {
wrapper.instance().handleIncrement(incrementSize);
expect(wrapper.state('testIncrement')).toEqual(propNumber + incrementSize);
});
And your handleIncrement function doesn't take an array, so no reason to pass it one.
In your parent component file, export the value, and then in your child component file, import the value

Passing value to props reactjs

I am trying pass value to my child components. The value that I am getting is coming from the an API that I called in my parent component and being called in the componentDidMount but the problem is the child components is not reading the props I am passing in his own componentDidMount, its only getting blank even in the reactdevtool it passing correct values. I solved this before but cannot remember what I did can you help. Thanks
Child:
componentDidMount() {
const {
events
} = this.props;
console.log(events)
}
Parent:
class App extends Component {
componentDidMount() {
let self = this;
GetAllMainItems().then(function(GetAllMainItemsResults) {
let MainObject = self.state.MainObject;
self.setState({
GetAllMainItemsResults
});
}
}
render() {
constructor() {
super();
this.state = {
MainObject: []
};
}
return ( <
div className = "App row" >
<
Calendar events = {
this.state.MainObject
}
/>
<
/div>
);
}
There are a few things you need to review.
constructor should be outside of render method.
You do not have to use let self = this. you can just do this.setState({...}) there.
Look at your GetAllMainItems callback. I don't know what you get
there. but you are definitely not setting mainObject in your state.
Instead, you will have this.state.GetAllMainItemsResults.
Recommendations
Try to understand object destructuring.
Use arrow functions
Hope it helps.
Parent Component
```
class App extends Component {
state = {
mainObject: ""
};
componentDidMount() {
GetAllMainItems().then(response => {
this.setState({
mainObject: response
});
});
}
render() {
const { mainObject } = this.state;
return (
<div className="App row">
<Calendar events={mainObject} />
</div>
);
}
}
The problem you are having is that your child component is re-rendering when it receives new events props.
Try adding a componentDidUpdate method to see these props updating:
componentDidUpdate(prevProps, prevState) {
console.log(prevProps, prevState);
console.log('events:', prevProps.events, this.props.events);
}

React State not available from Parent?

I have a form with a child component that renders as a table.
ParentComponent extends React {
state = {
anArray: []
}
<ParentComponent>
<table>
map ( thing => <ChildComponent {someFunction= this.updateFunction;} />
When ChildComponent maps the data to individual TD's. In my onChange in the ChildComponent, I'm invoking
onChange = this.props.someFunction();
and the code is hitting my breakpoint which is great. It calls someFunction in the ParentComponent. In someFunction, I'm trying to access the parent's state so I can match the onChanged TD with the proper index in the array but I'm getting undefined.
someFunction(id) {
const index = this.state.anArray.findIndex( x => x.id === id) ;
if (index === -1)
// handle error
console.log("DIDN'T FIND ID: " + id);
});
}
Why wouldn't I have access to state on the function invocation from the ChildComponent? I expected to be able to access it.
It's not clear from the posted code, but I guess you haven't bind the someFunction and you have the context of the child, instead of parent's.
ParentComponent extends React {
constructor(props){
super(props)
this.someFunction = this.someFunction.bind(this)
}
someFunction(){
...
}
render(){
...
}
}
If you have the necessary babel plugins you can even do
ParentComponent extends React {
someFunction = () => {
...
}
render(){
...
}
}

React child component can't get props.object

My parent component is like this:
export default class MobileCompo extends React.Component {
constructor(props) {
super(props);
this.state = {
data: null,
datasets: {}
};
this.get_data = this.get_data.bind(this);
}
componentWillMount() {
this.get_data();
}
async get_data() {
const ret = post_api_and_return_data();
const content={};
ret.result.gsm.forEach((val, index) => {
content[val.city].push()
});
this.setState({data: ret.result.gsm, datasets: content});
}
render() {
console.log(this.state)
// I can see the value of `datasets` object
return (
<div>
<TableElement dict={d} content={this.state.data} />
<BubbleGraph maindata={this.state.datasets} labels="something"/>
</div>
)
}
}
child component:
export default class BubbleGraph extends React.Component {
constructor(props) {
super(props);
this.state = {
finalData: {datasets: []}
};
console.log(this.props);
// here I can't get this.props.maindata,it's always null,but I can get labels.It's confusing me!
}
componentWillMount() {
sortDict(this.props.maindata).forEach((val, index) => {
let tmpModel = {
label: '',
data: null
};
this.state.finalData.datasets.push(tmpModel)
});
}
render() {
return (
<div>
<h2>{this.props.labels}</h2>
<Bubble data={this.state.finalData}/>
</div>
);
}
}
I tried many times,but still don't work,I thought the reason is about await/async,but TableElement works well,also BubbleGraph can get labels.
I also tried to give a constant to datasets but the child component still can't get it.And I used this:
this.setState({ datasets: a});
BubbleGraph works.So I can't set two states at async method?
It is weird,am I missing something?
Any help would be great appreciate!
Add componentWillReceiveProps inside child componenet, and check do you get data.
componentWillReceiveProps(newProps)
{
console.log(newProps.maindata)
}
If yes, the reason is constructor methos is called only one time. On next setState on parent component,componentWillReceiveProps () method of child component receives new props. This method is not called on initial render.
Few Changes in Child component:
*As per DOC, Never mutate state variable directly by this.state.a='' or this.state.a.push(), always use setState to update the state values.
*use componentwillrecieveprops it will get called on whenever any change happen to props values, so you can avoid the asyn also, whenever you do the changes in state of parent component all the child component will get the updates values.
Use this child component:
export default class BubbleGraph extends React.Component {
constructor(props) {
super(props);
this.state = {
finalData: {datasets: []}
};
}
componentWillReceiveProps(newData) {
let data = sortDict(newData.maindata).map((val, index) => {
return {
label: '',
data: null
};
});
let finalData = JSON.parse(JSON.stringify(this.state.finalData));
finalData.datasets = finalData.datasets.concat(data);
this.setState({finalData});
}
render() {
return (
<div>
<h2>{this.props.labels}</h2>
<Bubble data={this.state.finalData}/>
</div>
);
}
}

Resources