Replace useSelector with createStructuredSelector in react-native - reactjs

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.

Related

How to get redux store or redux dispatch in CLASS react component using typescript

I know how to to dispatch and get store using functional component. There is a great documentation about it here
But what about class component? How to get store and fire dispatch using class component. There is nothing said about it.
Lets say I defined hooks.ts:
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import { RootState, AppDispatch } from './store/store';
export const useAppDispatch = () => useDispatch<AppDispatch>()
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
And I need this functional component make as class:
import {useAppDispatch, useAppSelector} from "../../../hooks"
import { setRelease } from "./../store/factorySlice/factorySlice"
const X: React.FC = () => {
const {selectedRelease} = useAppSelector(store => store.factory)
const dispatch = useAppDispatch()
return (
<>
{selectedRelease}
<button onClick={() => dispatch(setRelease())}>Click</button>
</>
)
}
Redux Toolkit is our official recommended approach for writing Redux logic. It wraps around the Redux core, and contains packages and functions that we think are essential for building a Redux app
You can use it with class component by using react-redux's connect API:
import { connect } from "react-redux";
import { setRelease } from "./../store/factorySlice/factorySlice"
class X extends React.Component {
render() {
return (
<>
{this.props.selectedRelease}
<button onClick={() => this.props.dispatch(setRelease())}>Click</button>
</>
);
}
}
const mapStateToProps = (state) => ({
selectedRelease: state.factory.selectedRelease
});
export default connect(mapStateToProps)(X);
This was the first implementation. You would connect the class component to the store like
connect()(MyComponent)
Where Class is MyComponent

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)

console.log(this.props.store) returning undefined in my create react app?

I created a create react app and included redux with card lists and a searchbox that displayed the filtered results, the app was working before I added redux but now it isn't returning any results. When I console.log(this.props.store) it is returning undefined. I would really appreciate it if someone can help me with this. My files are as below:
constants.js
export const CHANGE_SEARCH_FIELD = 'CHANGE_SEARCH_FIELD';
actions.js
import {CHANGE_SEARCH_FIELD} from './constants.js';
export const setSearchField = (text) => ({
type: CHANGE_SEARCH_FIELD,
payload: text
})
reducer.js
import {CHANGE_SEARCH_FIELD} from './constants.js';
const intialState = {
searchField: ''
}
export const searchTeacher = (state=intialState, action={}) => {
switch(action.type) {
case CHANGE_SEARCH_FIELD:
return Object.assign({}, state, { searchField: action.payload });
default:
return state;
}
}
index.js
import ReactDOM from 'react-dom';
import './index.css';
import {Provider} from 'react-redux';
import {createStore} from 'redux';
import App from './App.js'; //Our main parent component
import {searchTeacher} from './reducer.js';
import 'tachyons';
import * as serviceWorker from './serviceWorker';
const store = createStore(searchTeacher)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root') );
serviceWorker.unregister();
App.js
import React, {Component} from 'react';
import {connect} from 'react-redux';
import CardList from './CardList.js';
import {teacher} from './teacher.js';
import Searchbox from './searchbox.js';
import ErrorBoundry from './ErrorBoundry';
import Scroll from './Scroll.js';
import './App.css';
import {setSearchField} from './actions.js';
const mapStateToProps = state => {
return {
searchField: state.searchField
}
}
const mapDispatchToProps = (dispatch) => {
return {
onSearchChange: (event) => dispatch(setSearchField(event.target.value))
}
}
class App extends Component {
constructor(){
super()
this.state = {
teacher: teacher, //teacher: [],
}
}
render(){
console.log(this.props.store);
const { searchField, onSearchchange } = this.props;
const filteredteacher= teacher.filter(
teacher =>{
return teacher.name.toLowerCase().includes(searchField.toLowerCase());
});
return(
<div className="tc">
<h1 className="f1"> Faculty Members ! </h1>
<Searchbox searchChange={onSearchchange} />
<Scroll>
<ErrorBoundry>
<CardList teacher={filteredteacher} />
</ErrorBoundry>
</Scroll>
</div>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
There won't be any props.store, because none of your code is passing down a prop named store to that component.
Components that have been wrapped in connect get props from three sources, combined:
Props passed from the parent component
Props returned from mapState
Props returned from mapDispatch
In this case, mapState is returning {searchField}, and mapDispatch is returning {onSearchChange}, and there's no props from the parent. So, the combined props are {searchField, onSearchChange}.
As a side note, you should use the "object shorthand" form of mapDispatch instead of writing it as a function:
const mapDispatch = {onSearchChange: setSearchField};
You will get two props from redux according to your code,
this.props.searchField
this.props.onSearchChange
connect function of react-redux used to connect react and redux.
mapDispatch is used to dispatch your actions which hold the payload(Second argument of connect function)
mapState is used to get the state of your properties(First argument of connect function)
So in your code, there is not any prop named store, Store is a global redux state which you can get with this method Store.getState() but here is store is redux store which you are passing here const store = createStore(searchTeacher) in your index.js file, This will show whole state of the redux store.
here is how you can get the state of your store.
How do I access store state in React Redux?
You will dispatch an action named onSearchChange like below in your on change method.
this.props.onSearchChange(e)
and redux will return you a value of this after storing in reducer with the name of this.props.searchField.
this.props.store would only be accessible if it was passed down from a parent component (which you are not doing here)
You create your store in index.js but you are not exposing an interface to it.
const store = createStore(searchTeacher);
You can expose these functions from your index.js file to reference the store:
export const getStore = () => store;
export const getState = () => { return store.getState(); };
Then from anywhere else (although not good practice):
import { getStore, getState } from 'index.js';

transitioning to use redux with hooks

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>

I am using React Context and would like to get confirmed my structure

It is my first application using react context with hooks instead of react-redux and would like to get help of the structure of the application.
(I'm NOT using react-redux or redux-saga libraries.)
Context
const AppContext = createContext({
client,
user,
});
One of actions example
export const userActions = (state, dispatch) => {
function getUsers() {
dispatch({ type: types.GET_USERS });
axios
.get("api address")
.then(function(response) {
dispatch({ type: types.GOT_USERS, payload: response.data });
})
.catch(function(error) {
// handle error
});
}
return {
getUsers,
};
};
Reducer (index.js): I used combineReducer function code from the redux library
const AppReducer = combineReducers({
client: clientReducer,
user: userReducer,
});
Root.js
import React, { useContext, useReducer } from "react";
import AppContext from "./context";
import AppReducer from "./reducers";
import { clientActions } from "./actions/clientActions";
import { userActions } from "./actions/userActions";
import App from "./App";
const Root = () => {
const initialState = useContext(AppContext);
const [state, dispatch] = useReducer(AppReducer, initialState);
const clientDispatch = clientActions(state, dispatch);
const userDispatch = userActions(state, dispatch);
return (
<AppContext.Provider
value={{
clientState: state.client,
userState: state.user,
clientDispatch,
userDispatch,
}}
>
<App />
</AppContext.Provider>
);
};
export default Root;
So, whenever the component wants to access the context store or dispatch an action, this is how I do from the component :
import React, { useContext } from "react";
import ListMenu from "../common/ListMenu";
import List from "./List";
import AppContext from "../../context";
import Frame from "../common/Frame";
const Example = props => {
const { match, history } = props;
const { userState, userDispatch } = useContext(AppContext);
// Push to user detail route /user/userId
const selectUserList = userId => {
history.push(`/user/${userId}`);
userDispatch.clearTabValue(true);
};
return (
<Frame>
<ListMenu
dataList={userState.users}
selectDataList={selectUserList}
/>
<List />
</Frame>
);
};
export default Example;
The problem I faced now is that whenever I dispatch an action or try to access to the context store, the all components are re-rendered since the context provider is wrapping entire app.
I was wondering how to fix this entire re-rendering issue (if it is possible to still use my action/reducer folder structure).
Also, I'm fetching data from the action, but I would like to separate this from the action file as well like how we do on redux-saga structure. I was wondering if anybody know how to separate this without using redux/redux-saga.
Thanks and please let me know if you need any code/file to check.
I once had this re-rendering issue and I found this info on the official website:
https://reactjs.org/docs/context.html#caveats
May it will help you too
This effect (updating components on context update) is described in official documentation.
A component calling useContext will always re-render when the context value changes. If re-rendering the component is expensive, you can optimize it by using memoization
Possible solutions to this also described
I see universal solution is to useMemo
For example
const Example = props => {
const { match, history } = props;
const { userState, userDispatch } = useContext(AppContext);
// Push to user detail route /user/userId
const selectUserList = userId => {
history.push(`/user/${userId}`);
userDispatch.clearTabValue(true);
};
const users = userState.users;
return useMemo(() => {
return <Frame>
<ListMenu
dataList={users}
selectDataList={selectUserList}
/>
<List />
</Frame>
}, [users, selectUserList]); // Here are variables that component depends on
};
I also may recommend you to completly switch to Redux. You're almost there with using combineReducers and dispatch. React-redux now exposes useDispatch and useSelector hooks, so you can make your code very close to what you're doing now (replace useContext with useSelector and useReducer with useDispatch. It will require minor changes to arguments)

Resources