No unused expressions on function inside componentDidMount - javascript

How can I fix or disable ESLint to ignore
[eslint] Expected an assignment or function call and instead saw an expression. (no-unused-expressions)
on this
authUser
? this.setState(() => ({ authUser }))
: this.setState(() => ({ authUser: null }));
Whilst, I understand the reason for the error and could just ignore it - as it's doing everything that it's is intended for.
Is there a way to remove this error from this document only, not globally? Or perhaps, is there a better way for me to rewrite this to avoid the error whilst achieving the same outcome?
Full Component
const withAuthentication = Component =>
class WithAuthentication extends React.Component {
constructor(props) {
super(props);
this.state = {
authUser: null
};
}
componentDidMount() {
firebase.auth.onAuthStateChanged(authUser => {
authUser
? this.setState(() => ({ authUser }))
: this.setState(() => ({ authUser: null }));
});
}
render() {
const { authUser } = this.state;
return (
<AuthUserContext.Provider value={authUser}>
<Component {...this.props} />
</AuthUserContext.Provider>
);
}
};
export default withAuthentication;

How about trying if then else?
firebase.auth.onAuthStateChanged(authUser => {
if (authUser)
this.setState(() => ({ authUser }))
else
this.setState(() => ({ authUser: null }));
});

Related

Not able to mock a constructor´s property: ReferenceError: EventSource is not defined (Jest, Enzyme)

In my test file I am mounting a Component and one of the nested Components is making me troubles. This is the Component:
class CacheHandler extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: true,
isLatestVersion: false,
refreshCacheAndReload: () => {
if (caches) {
caches.keys().then((names) => {
names.forEach((name) => {
caches.delete(name);
})
});
}
window.location.reload(true);
}
};
// ...some other code
}
render() {
const { loading, isLatestVersion, refreshCacheAndReload } = this.state;
return this.props.children({ loading, isLatestVersion, refreshCacheAndReload });
}
}
CacheHandler.propTypes = {
children: PropTypes.func.isRequired
};
export default CacheHandler;
I do not know how properly mock the constructor's refreshCacheAndReload property that gives me grey hair. It would be totally ok if it just does not do anything in the mock, but it shoud be found during the mounting process. At the moment, when I run my test, I get because of that part ReferenceError: EventSource is not defined
This is what I tried inside of my test but failed (Error: "CacheHandler" is read-only.):
const fakeCacheHandler = jest.fn(() => ({
constructor(props) {
//super(props);
this.state = {
loading: false,
isLatestVersion: false,
refreshCacheAndReload: () => { }
}},
render() {
const { loading, isLatestVersion, refreshCacheAndReload } = this.state;
return this.props.children({ loading, isLatestVersion, refreshCacheAndReload });
}
}))
CacheHandler = fakeCacheHandler;
I also tried to define the property directly in test but without success:
Object.defineProperty(CacheHandler, 'CacheHandler', {
value: jest.fn().mockImplementation(query => ({
loading: false,
isLatestVersion: false,
refreshCacheAndReload: () => {}
}))
})
I also tried to mock the whole module in the test like this:
jest.mock('../../components/utilities/CacheHandler', function() {
return jest.fn().mockImplementation(() => {
return {
refreshCacheAndReload: () => {},
render: () => {this.props.children({ loading:false, isLatestVersion:false, refreshCacheAndReload })},
}})
});
but still not successful.
jest.spyOn fails as well(Cannot spy the refreshCacheAndReload property because it is not a function; undefined given instead)
const fakeHandler = new CacheHandler();
const methodSpy = jest.spyOn(fakeHandler, "refreshCacheAndReload");
methodSpy.mockImplementation(() => {
console.log('test!');
})
This is how the test itself look like now:
it('renders MembersList', async () => {
const Component = withMemory(AdminAppCore, ROUTE_ADMIN_MEMBERS);
const result = mount(
<MockProvider stores={{ memberStore, programStore }}>
<Component />
</MockProvider>
);
console.log(result.debug());
await waitForState(result, state => state.loading === false);
expect(result.find(MembersList).length).toBe(1);
result.unmount();
});
I tried to mock the constructor of child element inside the test like this, but if failed(TypeError: this.props.children is not a function):
it('renders MembersList', async () => {
const Component = withMemory(AdminAppCore, ROUTE_ADMIN_MEMBERS);
const mockrefreshCacheAndReload = jest.fn(() => ({}));
const component = shallow(<CacheHandler/>);
component.setState({refreshCacheAndReload: mockrefreshCacheAndReload});
const result = mount(
<MockProvider stores={{ memberStore, programStore }}>
<Component />
</MockProvider>
);
console.log(result.debug());
await waitForState(result, state => state.loading === false);
expect(result.find(MembersList).length).toBe(1);
result.unmount();
});
So I am mounting the AdminAppCore, and inside of the AdminAppCore is the nested component that causes troubles.
Can anyone please explain me how can I mock the refreshCacheAndReload state inside of the nested Component constructor?
As I cannot post this in the comments, so please try this.
const mockrefreshCacheAndReload = jest.fn(() => ({}));
it('tests the method', () => {
const component = shallow(<CacheHandler/>);
component.setState({refreshCacheAndReload: mockrefreshCacheAndReload})
})

How to correctly use componentDidMount and componentDidUpdate in React.js?

I have a problem. I want to search for an index based on a url. Everything is sent to the components as it should, but there is an error after loading:
Cannot read property 'indexOf' of undefined
Data sent from JSON is sure to be transmitted and received correctly and is correctly assigned. The problem is most likely caused by badly applied 'componentDidMount' and 'componentDidUpdate'. How should it look correctly?
The data sent based on the URL of the page is 'this.props.brand'
Code:
class CarPage extends Component {
state = {
isLoading: true,
carData: [],
id: null
}
findMyIndex = () => {
this.setState({
id: this.carData.indexOf(this.props.brand),
})
}
componentDidUpdate() {
this.findMyIndex()
}
componentDidMount() {
fetch("/data.json")
.then(response => response.json())
.then(data => {
this.setState({
carData: data,
isLoading: false,
})
})
}
render() {
return (
<>
{!this.state.isLoading && (
<p>{this.state.carData[this.state.id].model}</p>
)}
</>
);
}
}
export default CarPage;
You don't need componentDidUpdate lifecycle method at all. You can do it like this:
class CarPage extends Component {
state = {
isLoading: true,
carData: [],
id: null
}
findMyIndex = () => {
return this.state.carData.map(el => el.brand).indexOf(this.props.brand);
}
componentDidMount() {
fetch("/data.json")
.then(response => response.json())
.then(data => {
this.setState({
carData: data,
isLoading: false,
})
})
}
render() {
return (
<>
{!this.state.isLoading && (
<p>{this.state.carData[this.findMyIndex(this.props.brand)].model}</p>
)}
</>
);
}
}
export default CarPage;
It seems that findMyIndex returns -1 and this.state.carData[this.state.id] is equal to undefined. Check if CarData indeed has a this.props.brand entry.

How to log both prevState and original state in React?

I have a toggle switch that goes from true to false.
flipSwitch = () => {
console.log(this.state)
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}))
}
Everything is working like it is supposed to, but I want to log both the prevState and original state at the same time. I tried below adding a callback function after setting the prevState, but then it breaks my toggle switch.
flipSwitch = () => {
console.log(this.state)
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}, () => console.log(prevState)))
}
Thats not correct what you're trying to do at here
flipSwitch = () => {
console.log(this.state)
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}, () => console.log(prevState)))
}
You won't have access to prevState in 2nd parameter of setState.
You should modify your setState function like this
flipSwitch = () => {
console.log(this.state) // this refers to previous state here
this.setState(prevState => {
console.log(prevState) // prevState refers to previous state
return ({
isToggleOn: !prevState.isToggleOn
})
}, () => console.log(this.state) // here this refers to updated state)
}
E.g you can try
import React from "react";
class App extends React.Component {
constructor() {
super();
this.state = {
isToggleOn: false
};
}
flipSwitch = () => {
console.log(this.state);
this.setState(
prevState => {
console.log("prevState", prevState);
return {
isToggleOn: !prevState.isToggleOn
};
},
() => {
console.log("setState Callback", this.state);
}
);
};
render() {
return (
<div className="App">
<button onClick={this.flipSwitch}>
{this.state.isToggleOn ? "Flipped" : "Flip Switch!"}
</button>
</div>
);
}
}
export default App;

React-native redux - this.props are undefined from AsyncStorage

Being a newbie with RN and Redux, I'm confused as to why my props are undefined after reading from AsyncStorage.
I log in, save the state to the store and storage... I reload the app and read from the storage and update the state. The storage is retrieving my object but the props are undefined.
actions.js:
export const getSession = (data) => ({
type: 'GET_SESSION',
payload: {
user: data
}
});
export const getUserSession = () => dispatch => {
return AsyncStorage.getItem('userSession').then((data) => {
console.log('Props at asynsstorage: ', data);
// {"current_user":{"uid":"1","roles":["authenticated","administrator"], ...}
dispatch(loading(false));
dispatch(getSession(data));
})
.catch((err) => {
})
}
reducer.js
import { combineReducers } from 'redux';
const defaultState = {
xcsrf: '',
user: {},
loading: false,
error: '',
};
const authReducer = ( state = defaultState, action ) => {
switch(action.type) {
case 'GET_SESSION':
return {
...state,
user: action.payload.user,
loading: false,
}
case 'SAVE_SESSION':
return {
...state,
user: action.payload.user,
loading: false,
}
default:
return state;
}
}
export default combineReducers({
authReducer: authReducer
});
authLoading.js // screen
class AuthLoadingScreen extends React.Component {
constructor() {
super();
}
componentDidMount = () => {
this.props.getUserSession().then(() => {
console.log( 'Props at loading: ', this.props.user );
// undefined
})
.catch(error => {
})
};
// Render any loading content that you like here
render() {
return ();
}
}
const mapStateToProps = state => ({
user: state.user,
});
const mapDispatchToProps = dispatch => ({
getUserSession: () => dispatch(getUserSession()),
});
export default connect(mapStateToProps, mapDispatchToProps)(AuthLoadingScreen);
You cannot access directly user of reducer. So change
const mapStateToProps = state => ({
user: state.user,
});
To
const mapStateToProps = state => ({
user: state.authReducer.user,
});
And one more thing AsyncStorage's getItem() method return string of stored data. You have not converted to it json. So please also convert that as below :
export const getUserSession = () => dispatch => {
return AsyncStorage.getItem('userSession').then((data) => {
console.log('Props at asynsstorage: ', data);
// {"current_user":{"uid":"1","roles":["authenticated","administrator"], ...}
dispatch(loading(false));
dispatch(getSession(JSON.parse(data))); //convert to json here
})
.catch((err) => {
})
}

POST http://localhost:3000/api/courses/[object%20Object]/units 404 (Not Found)

(Only my 3rd post here, so please excuse any blatant issues).
The following is my Unit component, a child of a Course component (courses has_many units).
import React from 'react';
import { connect } from 'react-redux';
import { getUnits, addUnit, updateUnit } from '../reducers/units';
import { Container, Header, Form } from 'semantic-ui-react';
class Units extends React.Component {
initialState = { name: ''}
state = { ...this.initialState }
componentDidUpdate(prevProps) {
const { dispatch, course } = this.props
if (prevProps.course.id !== course.id)
dispatch(getUnits(course.id))
}
handleSubmit = (e) => {
debugger
e.preventDefault()
debugger
const unit = this.state
const { dispatch } = this.props
if (unit.id) {
debugger
dispatch(updateUnit(unit))
} else {
debugger
dispatch(addUnit(unit))
this.setState({ ...this.initialState })
}
}
handleChange = (e) => {
const { name, value } = e.target
this.setState({ [name]: value })
}
units = () => {
return this.props.units.map( (unit, i) =>
<ul key={i}>
<li key={unit.id}> {unit.name}</li>
<button>Edit Module Name</button>
<button>Delete Module</button>
</ul>
)
}
render() {
const { name } = this.state
return (
<Container>
<Header as="h3" textAlign="center">Modules</Header>
{ this.units() }
<button>Add a Module</button>
<Form onSubmit={this.handleSubmit}>
<Form.Input
name="name"
placeholder="name"
value={name}
onChange={this.handleChange}
label="name"
required
/>
</Form>
</Container>
)
}
}
const mapStateToProps = (state) => {
return { units: state.units, course: state.course }
}
export default connect(mapStateToProps)(Units);
The following is its reducer:
import axios from 'axios';
import { setFlash } from './flash'
import { setHeaders } from './headers'
import { setCourse } from './course'
const GET_UNITS = 'GET_UNITS';
const ADD_UNIT = 'ADD_UNIT';
const UPDATE_UNIT = 'UPDATE_UNIT';
export const getUnits = (course) => {
return(dispatch) => {
axios.get(`/api/courses/${course}/units`)
.then( res => {
dispatch({ type: GET_UNITS, units: res.data, headers: res.headers })
})
}
}
export const addUnit = (course) => {
return (dispatch) => {
debugger
axios.post(`/api/courses/${course}/units`)
.then ( res => {
dispatch({ type: ADD_UNIT, unit: res.data })
const { headers } = res
dispatch(setHeaders(headers))
dispatch(setFlash('Unit added successfully!', 'green'))
})
.catch( (err) => dispatch(setFlash('Failed to add unit.', 'red')) )
}
}
export const updateUnit = (course) => {
return (dispatch, getState) => {
const courseState = getState().course
axios.put(`/api/courses/${course.id}/units`, { course })
.then( ({ data, headers }) => {
dispatch({ type: UPDATE_UNIT, course: data, headers })
dispatch(setCourse({...courseState, ...data}))
dispatch(setFlash('Unit has been updated', 'green'))
})
.catch( e => {
dispatch(setHeaders(e.headers))
dispatch(setFlash(e.errors, 'red'))
})
}
}
export default (state = [], action) => {
switch (action.type) {
case GET_UNITS:
return action.units;
case ADD_UNIT:
return [action.unit, ...state]
case UPDATE_UNIT:
return state.map( c => {
if ( c.id === action.unit.id )
return action.unit
return c
})
default:
return state;
}
};
Note: My reducer is working for my getUnits and rendering the units properly.
Note also: when I try to submit a new unit, it ignores all of the debuggers in my handleSubmit and the debuggers in my addUnits (in the reducer), but somehow renders the flash message of "Failed to add units".
Then the console logs the error seen in the title of this post.
I raked my routes and my post is definitely supposed to go to the route as it is.
I have tried passing in the unit and the course in various ways without any change to the error.
How can it hit the flash message without hitting any of the debuggers?
How do I fix this [object%20Object]issue?
Thanks in advance!
The variable course in the following line
axios.get(`/api/courses/${course}/units`)
is an object. When you try to convert an object to a string in JavaScript, [object Object] is the result. The space is then converted to %20 for the URL request.
I would look at the contents of the course variable. Likely, what you actually want in the URL is something inside of course. Perhaps course.id.
If you are still having issues, you'll need to explain what value should go in the URL between /courses/ and /units, and where that data exists.
You are invoking addUnit and updateUnit with a parameter that is equal to this.state in handleSubmit
const unit = this.state
addUnit(unit)
As this.state is of type object, it is string concatenated as object%20object.
getUnit works fine as the parameter passed there comes from the prop course. Check the value of state inside handleSubmit.

Categories

Resources