transitioning to use redux with hooks - reactjs

figuring out how to use redux with hooks using this way but not sure its the correct way
import React, { useState, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { getPins } from "../../../actions/pins";
function MainStory() {
const pins = useSelector(state => state.pins.pins);
const dispatch = useDispatch();
useEffect(() => {
dispatch(getPins(pins));
}, []);
console.log(pins);
return(<div> test </div>
would the above way be the right way to go despite missing dependencies?
React Hook useEffect has missing dependencies: 'dispatch' and 'pins'. Either include them or remove the dependency array
with components (the way i had it before)
import { getPins } from "../../actions/pins";
import { connect } from "react-redux";
import PropTypes from "prop-types";
export class Pins extends Component {
static propTypes = {
pins: PropTypes.array.isRequired,
getPins: PropTypes.func.isRequired
};
componentDidMount() {
this.props.getPins();
}
render() {
console.log(this.props.pins);
return <div>test</div>;
}
}
const mapStateToProps = state => ({
pins: state.pins.pins
});
export default connect(mapStateToProps, { getPins })(Pins);
the plan is to list each pin

You can add dispatch to the dependencies list, since it won't change. If you'll add the pins to dependancies list of the useEffect block, you might cause infinite loop, if the response to the action changes the state (call to server that returns an array).
However, according to the class component example, the getPins() action creator doesn't require the value of pins, so you can just add dispatch to the list of dependancies:
function MainStory() {
const pins = useSelector(state => state.pins.pins);
const dispatch = useDispatch();
useEffect(() => {
dispatch(getPins());
}, [dispatch]);
console.log(pins);
return(<div> test </div>

Related

How do you implement bindActionCreators in class component with out using "connect"

I'm trying to find a way to convert this code to class based component:
import React, {useEffect} from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { bindActionCreators } from 'redux'
import { addComments } from './state/actions/commentActions'
export default function App() {
const state = useSelector((state) => state)
const dispatch = useDispatch()
const {commentData} = state
console.log(commentData);
const add = bindActionCreators(addComments, dispatch)
useEffect(() =>{
async function fetchData(){
const data = await fetch('https://jsonplaceholder.typicode.com/comments?_limit=10').then(data => data.json())
add(data)
}
fetchData()
}, [])
const carr = commentData.map(data => <h1>{data.body}</h1>)
return (
<div>
{carr}
</div>
)
}
I wasn't using class based components when I first learnt redux so I don't know how to replace those hooks other than connect HOC, unfortunately I've put in the position where I can't use them. So how do you implement bindActionCreators on a class based components?
My first question is, why are you trying to convert a function component into a class component? Normally, people are trying to convert classes into functions :)
That said, to specifically answer your question: you don't have to call bindActionCreators yourself - connect will do it internally:
https://react-redux.js.org/using-react-redux/connect-mapdispatch#defining-mapdispatchtoprops-as-an-object
import { increment, decrement, reset } from './counterActions'
const actionCreators = {
increment,
decrement,
reset,
}
export default connect(mapState, actionCreators)(Counter)

React hooks useState getting diferrent value from redux state

I have react component look like this following code:
import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Link, useParams } from "react-router-dom";
import { createClient, getClients } from "../redux/actions/clients";
function UpdateClient(props) {
let params = useParams();
const { error, successSubmit, clients } = useSelector(
(state) => state.clients
);
const [client, setClient] = useState(clients[0]);
const dispatch = useDispatch();
useEffect(() => {
dispatch(getClients({ id: params.id }));
}, []);
const submitClient = () => {
dispatch(createClient(client));
};
return (
<div>{client.name} {clients[0].name}</div>
);
}
export default UpdateClient;
And the result is different client.name return test1,
while clients[0].name return correct data based on route parameter id (in this example parameter id value is 7) which is test7
I need the local state for temporary saving form data. I don't know .. why it's become different?
Can you please help me guys? Thanks in advance
You are referencing a stale state which is a copy of the clients state.
If you want to see an updated state you should use useEffect for that.
useEffect(() => {
setClient(clients[0]);
}, [clients]);
Notice that duplicating state is not recommended.
There should be a single “source of truth” for any data that changes in a React application.

Replace useSelector with createStructuredSelector in react-native

I use reselect library with my react-native project. I have the following code.
import { createSelector } from 'reselect';
import { ApplicationState } from 'types/reducer.types';
const selectAuth = ({ auth }: ApplicationState) => auth;
export const selectFirstName = createSelector([selectAuth], auth => auth.user?.firstName);
export const selectLastName = createSelector([selectAuth], auth => auth?.user?.lastName);
In the container the code is like this.
import { connect } from 'react-redux';
// import { useSelector } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { signOut } from 'Redux/auth/auth.actions';
import { selectFirstName, selectLastName } from 'Redux/auth/auth.selectors';
import CustomDrawer from './custom-drawer.component';
const mapStateToProps = createStructuredSelector({
firstName: selectFirstName,
lastName: selectLastName,
});
export default connect(mapStateToProps, { signOut })(CustomDrawer);
Now I want to use useSelector hook. I tried several ways but it didn't work. How can this be done?
createStructuredSelector returns a selector (a function that takes state and returns an object {firstName, lastName}), so you can use it inside of useSelector
const {firstName, lastName} = useSelector(createStructuredSelector({
firstName: selectFirstName,
lastName: selectLastName,
}));
or using your existing variable
const {firstName, lastName} = useSelector(mapStateToProps);
However there's not really any point in combining and then destructing the properties when you can use many useSelector hooks in one component.
const firstName = useSelector(selectFirstName);
const lastName = useSelector(selectLastName);
useSelector and useDispatch are two hooks that can be imported from react-redux package and these two can easily replace the need for using connect.
Here is how you can use them:
import { useDispatch, useSelector } from 'react-redux';
import {someAction} from '../user/userActions';
const YourComponent = () => {
// useDispatch returns back a dispatch function
const dispatch = useDispatch();
// useSelector selects a portion of the state, it plays the role and
// replace the need for mapStateToProps in connect higher order function
const currentUser = useSelector(state => state.user.currentUser);
return(
<>
<h1>{ currentUser.firstName }</h1>
// Here you can use dispatch to dispatch an action
<button onClick={() => dispatch(someAction)}></button>
</>
)
};
export default YourComponent;
Overall, you can see that by using useSelector and useDispatch we removed so much boilerplate from our code(connect, mapStateToProps, mapDispatchToProps, createStructuredSelector removed) and it looks clean and more readable and still works as previous.
Here is the official resource you can read more about the two above-mentioned hooks.
React-Redux Hooks
P.S. You can also send a selector created using reselect or whatever selector of your choice into useSelector.

How to write a test for conditional rendering component depended on useState hook in React?

I'm trying to write a test for my functional component, but don't understand how to mock isRoomsLoaded to be true, so I could properly test my UI. How and what do I need to mock?
import React, { useState, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchRooms } from '../../store/roomsStore'; // Action creator
// Rooms component
export default ({ match, location, history }) => {
const roomsStore = useSelector(state => state.rooms);
const dispatch = useDispatch();
const [isRoomsLoaded, setRoomsLoaded] = useState(false);
useEffect(() => {
const asyncDispatch = async () => {
await dispatch(fetchRooms());
setRoomsLoaded(true); // When data have been fetched -> render UI
};
asyncDispatch();
}, [dispatch]);
return isRoomsLoaded
? <RoomsList /> // Abstraction for UI that I want to test
: <LoadingSpinner />;
};
If you want, you could flat out mock useState to just return true or false, to get whichever result you want by doing the following.
const mockSetState = jest.fn();
jest.mock('react', () => ({
...jest.requireActual('react'),
useState: value => [true, mockSetState],
}));
By doing this, you're effective mocking react, with react, except useState, its a bit hacky but it'll work.

is there another way to mock component's mapDispatchToProps when using Jest

I currently have a component like so:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { getDataAction } from ' './my-component';
export class MyComponent extends { Component } {
componentWillMount() {
this.props.getData();
}
render(){
<div>
this.props.title
</div>
}
}
const mapStateToProps = (state) => ({
title: state.title
});
const mapDispatchToProps = (dispatch) ({
getData() {
dispatch(getDataAction());
}
});
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)
and I am trying to shallow render test it using jest and enzyme.
test:
import React from 'react';
import { shallow } from 'enzyme';
import { MyComponent } from './index';
it('renders without crashing', () => {
shallow(<MyComponent getData={jest.fn()} />);
});
My question is, is this the conventional way to mock? Jest official docs don't mention specifically about mocking props and this post Using Jest to mock a React component with props is about testing with full mounting instead.
Is there another way to mock dispatchToProps? In this example there is only one, but what if I have a lot of functions in dispatchToProps?
Side Question: in my real file, I have a reference to a value like this.props.information.value which I expect to throw an error like cannot get value of undefined since information is not mocked/defined, but it doesn't. It's only when functions are not present that an error is thrown.
You can export mapDispatchToProps and write tests for it by importing it in your tests.
Add export { mapDispatchToProps }; at the end of your MyComponent.js
Create MyComponent.tests.js file beside MyComponent.js
import configureMockStore from 'redux-mock-store';
import thunkMiddleware from 'redux-thunk';
import { mapDispatchToProps } from './MyComponent';
const configMockStore = configureMockStore([thunkMiddleware]);
const storeMockData = {};
const mockStore = configMockStore(storeMockData);
describe('mapDispatchToProps', () => {
it('should map getDataAction action to getData prop', () => {
// arrange
const expectedActions = [getDataAction.type];
const dispatchMappedProps = mapDispatchToProps(mockStore.dispatch);
// act
dispatchMappedProps.getData();
// assert
expect(mockStore.getActions().map(action => action.type)).toEqual(expectedActions);
}
});
Here I have used thunk, just to let you know that how to do it if there are middlewares configured in your store setup.
Here getDataAction can also be a function instead of a simple action like { type: 'FETCH_DATA' } if you are using middlewares like thunks. However, the approach to test is same except that you will create expectedActions with explicit action types like const expectedActions = ['FETCH_CONTACTS']
Here FETCH_CONTACT is another action dispatched in your thunk i.e getDataAction

Resources