I would like to ask for your opinion about this. Basically what I want now is to refresh the component list after I remove an item on the object lists. Currently I can successfully removed an item via deleteHeroes(list,index) function but my component is not refreshing at all to reflect the removed item. Can you should some light on how can I do it? here's my code below:
componentDidMount(){
// Fetch lists of heroes
this.props.getAllHeroes();
}
renderHeroesList(){
var listData = this.props.heroes.map((heroes,index) => (
<HeroesListComponent key={heroes.id} heroes={heroes} onDelete = { () => this.deleteHeroes(heroes,index)} />
));
return listData;
}
// Remove item on heroes list
deleteHeroes(list,index){
const heroesProps = this.props.heroes;
heroesProps.splice(heroesProps.indexOf(index), 1);
}
render(){
return(
{ this.renderHeroesList() }
);
function mapStateToProps(state){
return {
heroes: state.heroes.data
}
}}
function mapDispatchToProps(dispatch){
return bindActionCreators ({
getAllHeroes: getAllHeroes,
deleteHero: deleteHero,
}, dispatch);
}
You have a side effect, which should be avoided at all cost. In your case, it is that you are mutating the internal reference of heroes props. So typical plan to avoid this problem is to clone the props and then dispatch a new action with the new props data. So your code should look like:
deleteHeroes(list,index){
const clonedHeroesProps = this.props.heroes.slice(0); // clone the array
heroesProps.splice(heroesProps.indexOf(index), 1);
dispatch({type: 'SAVE_HEROES', heroes: clonedHeroesProps});
}
The better, more Reactish way would be by using Immutability Helpers:
deleteHeroes(list,index){
const clonedHeroesProps = update(heroesProps, {$splice: [[heroesProps.indexOf(index), 1]]});
dispatch({type: 'SAVE_HEROES', heroes: clonedHeroesProps});
}
Because you did not notify anything about your change.
You must dispatch an action after deleteHeroes, something like this
deleteHeroes(list,index){
const { heroesProps, dispatch }= this.props;
heroesProps.splice(heroesProps.indexOf(index), 1);
dispatch({type: 'SAVE_HEROES', heroes: heroesProps});
}
// and somewhere in reducer
case SAVE_HEROES:
return {...state, heroes: action.heroes}
and write the appropriate function to reducer.
But let component delete hereos (alter state), you broke the idea of redux.
Instead, the component should not directly modify heroes, you dispatch an action like 'DELETE_HEROES' and let reducer do the rest.
You have 2 main options to consider:
put the data (state) to shared common ancestor, that is standard strategy from react: https://facebook.github.io/react/docs/lifting-state-up.html
put all the state to Redux (https://github.com/reactjs/react-redux), then u connect your component and display based on the redux state. this case, u do not have to do any thing after deleting, redux framework will take care of the data flow and ui refresh
Related
I am trying to learn redux. I think I have reducers down pretty well, I can pull the data from the store and set it via props.
But I cannot wrap my head around actions and changing data on the state of the store.
I have this:
const mapDispatchToProps = (dispatch) => {
return{
what goes in here?
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ComponentName);
I need to know how to dispatch a change inside the const. just a simple add text to an empty state. example: the state is apples:'', and I want to add 'red delicious' to that.
mapDispatchToProps provides you a way to connect your action creators to your components. Let's assume you have an action creator which increments a counter state
export const change = value =>({
type: 'CHANGE_FRUIT',
fruit : value
})
And you want value to be passed from one of your components. First use connect HOC in this component like you're already doing. Now you need to import incrementCounter from your actions folder
import { change as changeFruit } from './actions/fruit'
Now use mapDispatchToProps like this
const mapDispatchToProps = dispatch =>({
change : fruit => dispatch(changeFruit(fruit))
})
Now you have an action creator serialized inside your component's props and when you call props.increment(2) this will be the equivalent to call
dispatch(changeFruit('apple'))
Here is why you should always do props.increment instead of directly call dispatch inside your component.
So the full implementation inside your component could be like this
import { change as changeFruit } from './actions/fruit'
class Component extends React.component{
render(){
const { fruit, change } = this.props
return(
<>
<div>{fruit}</div>
<button onClick={() => change('orange')}>Change to orange</button>
</>
)
}
}
const mapStateToProps = state =>({
fruit : state.fruit
})
const mapDispatchToProps = dispatch =>({
change : fruit => dispatch(changeFruit(fruit))
})
export default connect(mapStateToProps, mapDispatchToProps)(Component)
Your reducer should look like this
const initialState = {
fruit: 'apple'
}
export const reducer = (state = initialState, action) =>{
switch(action.type){
case 'CHANGE_FRUIT' : return{
...state,
fruit : action.fruit
}
default : return state
}
}
An "action" is just a plain object that describes some sort of change you want to happen in your store. So in your case you want to add text to your string, such an action might look like:
{
type: 'ADD_TEXT',
value: 'red delicious'
}
An action creator is nothing more than a function that returns such a plain object. Notice we can generalise the action creator, so you can pass in the string to add, rather than have it hard coded as 'red delicious'.
const addText = value => ({ type: 'ADD_TEXT', value })
In order to 'send' this action to the reducers, it needs to be passed to dispatch function that Redux provides. E.g. this would dispatch the action:
const action = addText('red delicious') // Create the plain action object
dispatch(action) // Send it to the reducers
That can get kind of tedious to write out manually all the time, so mapDispatchToProps helps with that. It's a function you provide to connect that does the above all in one place, leaving the rest of your component code un-cluttered. Redux calls it internally to generate the props for your connected component. So when you do,
const mapDispatchToProps = dispatch => ({
addText: value => {
// Same code as above:
const action = addText('red delicious')
dispatch(action)
}
})
In your component you can call
props.addText('red delicious')
look when use the connect function in the app it takes 2 arguments the first is the state current and the second argument is the dispatch that specifies what type of action that will be implemented is the specific action the dispatch depended on the despatch will be called the specific action that link by reducer that implements in provider in-store when use
<Provider store={store}>
which the reducer that created by the const store=createStore(rootReducer)
const mapDispatchToProps = (dispatch) => {
return{
example look this is dispatch that well happened it will call specif action bu use key type
this == store.dispatch(constants.WIDITHROUP)
the first is key or name of the handle when you want to implement :(input that enters fro this dispatch and=> it call store.dispatch(name of Handel) )
widthroup: (balance) => dispatch({ type: constants.WIDITHROUP ,balance}),
deposit: (balance) => dispatch({ type: constants.DEPOSIT,balance }) }
}
}
Basically I am answering for the part where you have confusion:
return {
what goes in here?
}
Ideally, if you think as class concept, let's say you have a class that facilitates to choose and save your favorite color selection.
So, all you want is to create a class component and define a method saveSelection that should save selection to store. And, who ever wants to get that selection or see your selected colors they can access from store.
Back to Redux, the rule says, you can not update the state of store directly. You have to dispatch an action.
So, if I already have a decided to create a method saveSelection for that purpose, what should I do, so that it should dispatch an action.
May be something like:
class ColorSelector {
saveSelection () {
dispatch(action); //dispatch is some way to dispatch an action
}
}
But, action is not just a command or text or a value, we need to specify, the type of action (because reducer need to identify which data it need to update based on action) and the data I want to save. That will make sense of an action.
So, I want something like:
//I pass my selected color to saveSelection method and that should save
saveSelection(dataToUpdate) {
dispatch({type: actionType, data: dataToUpdate})
}
One thing may be going inside your head -> How will I get dispatch and how did I just use it here?
That will get clear in a sec, but first lets replace the dispatch arguments which we can write like:
saveSelectionAction(dataToUpdate) {
return {type: actionType, data: dataToUpdate}
}
And our method would be like:
saveSelection(dataToUpdate) {
dispatch(saveSelectionAction(dataToUpdate))
}
And saveSelectionAction becomes the action creator part. Now comes the dispatch part. So, lets wrap this method with another method which provides dispatch and expose saveSelection
anotherMethodWhichPassesDispatch(dispatch) {
// unleash the power of closure
return {
saveSelection: saveSelection(dataToUpdate) {
dispatch(saveSelectionAction(dataToUpdate))
}
}
}
or more correctly
// Method to map dispatching action to components props
anotherMethodWhichPassesDispatch(dispatch) {
// return list of your created action you want as props
return {
// Name it whatever you want and that will be a void method passing data
saveSelection: (dataToUpdate) => {
// pass the action creator's result which is defined above
// Or, exported from a separate file
dispatch(saveSelectionAction(dataToUpdate))
}
}
}
Now pass this method as argument to connect HOC and that will map saveSelection to a props and provide dispatch to your method.
connect(mapStateToProps, anotherMethodWhichPassesDispatch)(<container>) or we can rename it as mapDispatchToProps.
Now go back to your class's saveSelection method and do like:
saveSelection = (selectedColor) => {
this.props.saveSelection(selectedColor);
}
That's it.
I am currently using this pattern, but it feels a bit wrong. I don't think a redux thunk should return a string, and the component should only access the data fetched through the connected props. I may be wrong, but is there a better, more idiomatic way of doing this?
I want to keep the two thunks separate so that I can call each one individually.
// actions
const putCustomer = ...;
const putBooking = ...;
// thunks
export async function fetchCustomer(customerId) {
return dispatch => {
const customer = await customerApi.fetch(customerId);
dispatch(actions.putCustomer( {customer} ));
};
}
export async function fetchBooking(bookingId) {
return dispatch => {
const booking = await bookingApi.fetch(bookingId);
dispatch(actions.putBooking( {booking} ));
// I AM RETURNING THE BOOKING DATA HERE
return booking;
};
}
// component
class MyComponent extends React.Component {
async componentWillMount() {
const booking = await this.props.dispatch(fetchBooking(this.props.bookingId));
// I WANT TO ACCESS THE BOOKING DATA HERE
this.props.dispatch(fetchCustomer(booking.customerId));
}
}
QUESTION UPDATED
Added some comments where I want to access the data
In Ideal case below is the how the flow goes incase you dispatch some action :
It is suggested that UI can be bind to state and state is been modified by reducer , its a single direction flow. The data you are trying to return can be managed via state and the dispatcher can modify that state
After some researches, I found some questions on stackoverflow about what I am trying to achieve, however, I don't feel that these questions and their answers gives me the "answers" or the "directions" i am looking for..
Note: I am pretty new to react even if I already made 2 projects and implemented redux into one of them. However, I ain't new at all in C# or in Go, even less in C. Based on my experience, I am just used to some architectures and I would like to reproduce one of them.
Here is a pretyy good schema from a similar question of mine:
Situation:
So let say I have pages that contains Components. I want these pages/compoments to display some stuff. One of my functionnality is to discover a map and for that, when the client moves, he gets new parts from my API. However, I don't wanna ask the server to give me the new parts and the ones I discovered already.
My idea about it would be to use a service MapService.js. This one would just store the discovered pieces of the map discovered and ask the server automatically about the new ones, and of course, store the new ones (concat).
However, I have to be logged for this, so I would like an ApiService.js that would store my authentication data and automatically put them in each of my requests.
Based on what I said, we would have something as:
Page -> Component -> Service -> API
From this, the API response would be gotten by my service, handled, then returned to the component. Handled means (data added to the previous then all returned)
I saw on internet one question that was referring "MVCS" (Model View Controller Service) pattern and I think I am looking for something as but I am not sure about how to implement it in ReactJs.
Redux seems to be something that you put all around and everywhere in your solution. What I would like is to use it as a "repository" let say, to be able to manage it from a service and not from the component itself. However, a service should be a single instance shared across the app and I don't know if something such as dependency injection could be the solution in ReactJS
Feel free to ask any edit if you need more details :)
Thanks for your help !
Here is a minimal example of Redux middleware usage. Usually, redux devs are using libraries (that give you a middleware) to have access to more appropriate APIs.
Redux middleware are chained, so each middleware can call the next middleware. The first middleware of the chain is called every time dispatch function (you can have it from react-redux connect) is called. In a middleware, if there is no next middleware it is the reducers that will be called. The next middleware can be call asynchronously after receiving an action. (Redux docs will still be better than my explainations).
In my example there is a catService that provide function that call rest API. Your services can be anything (a Class instance or a singleton for example). Usually in React/Redux stack, devs don't use object oriented development.
If a component dispatch getCat(123), the catMiddleware will be called (synchronously). Then requestGetCat will be called with the id 123. When the promise returned by requestGetCat will be resolved a setCat action will be send through the reducers to update the redux state. Once the redux state is done, the component listening for cats items object will be update too (triggering a rerender).
That can look very complexe, but in fact, it is very scalable and convenient.
// catService.js
// return a promise that return a cat object
const requestGetCat = id =>
fetch(`www.catcat.com/api/cat/${id}`)
.then(response => response.json())
// catTypes.js
export const GET_CAT = 'GET_CAT'
export const SET_CAT = 'SET_CAT'
// catActions.js
export const getCat = id => ({
type: GET_CAT,
id
})
export const setCat = (cat, id) => ({
type: SET_CAT,
id,
cat
})
// catReducer.js
const initialState = {
items: {}
}
const catReducer = (state = initialState, action) => {
if (action.type === SET_CAT) {
return {
items: {
...state.items,
[action.id]: action.cat
}
}
}
}
// catMiddleware.js
const handleGetCat = (next, action) => {
requestGetCat(action.id)
.then(cat => next(setCat(cat, action.id)))
// after retrieving the cat send an action to the reducers (or next middleware if it exist)
}
const actionHandlers = {
[GET_CAT]: handleGetCat
}
// receive every actions passing by redux (if not blocked)
// store: { dispatch, getState }
// next: next middleware or reducers (that set redux state)
// action: a redux action (dispatched) with at least type property
const catMiddleware = store => next => action => {
const handler = actionHandlers[action.type]
if (handler) {
handler(next, action)
} else {
// passing the action to the next middleware (or reducer - when there is no next middleware)
next(action)
}
}
// you have to apply your middleware
// and your reducer (see redux doc)
This one would just store the discovered pieces of the map discovered and ask the server automatically about the new ones, and of course, store the new ones
This is something I've wanted to do in the past, but never implemented a solution for.
The issue is that you essentially want to "cross the streams"..
In Redux there are two separate streams, ie dispatch an action to update the store, and read data from the store. Each of these are executed separately from a component. Combined, they can be used in a cycle by calling an action to load data into the store which triggers an update of the component which then reads from the store.
Basically you can't have non-component code that reads from the store, and if the data is missing, fires an action to load the data, then returns the data.
Thinking about it now, I'm wondering if the way to do this without adding logic to your view component is to wrap it in a component (HOC) that provides the logic.
The HOC will check the state for the location specified in the props. If it doesn't find it, it will dispatch an action to fetch it and render a loading display. When the state is updated with the new location it will update and render the wrapped component.
You could optionally always render the wrapped component and have it cope with the missing location until it is updated with the location set..
untested brain-dump below
loader HOC:
import React, { useEffect } from "react";
import actions from "./actions";
function withLocationLoader(Component) {
const Wrapper = function ({ location, locations, loadLocation, ...props }) {
useEffect(() => {
if (!locations[location]) {
loadLocation(location);
}
}, [locations]);
if (locations[location]) {
return <Component locations={locations} {...props} />;
}
return <div>Loading...</div>;
}
const mapStateToProps = (state, ownProps) => {
return { locations: state.locations };
};
const mapActionsToProps = {
loadLocation: actions.loadLocation,
};
return connect(
mapStateToProps,
mapActionsToProps
)(Wrapper);
}
export { withLoader };
component:
function MyBareComponent({ locations }) {
return <div>{JSON.stringify(locations)}</div>;
}
const MyComponent = withLocationLoader(MyBareComponent);
export { MyComponent };
actions: (utilising redux-thunk middleware)
function setLocation(location, data) {
return { type: "SET_LOCATION", payload: { location, data } };
}
export function loadLocation(location) {
return dispatch =>
Promise.resolve({ geoData: "" }) // mock api request
.then(data => dispatch(setLocation(location, data)));
}
In my react app I have component named profile, and I am fetching data from server and showing it inside that component. I am using redux and redux-thunk along with axios. With help of mapDispatchToProps function, i am calling redux action for fetching that data when component is mounted and saving it to redux state. After that, using mapStateToProps function i am showing that data on the screen via props. That works fine. Now I want to have possibility to edit, for example, first name of that user. To accomplish that i need to save that data to component state when data is fetched from server, and then when text field is changed, component state also needs to be changed. Don't know how to save data to component sate, immediately after it is fetched.
Simplified code:
const mapStateToProps = (state) => {
return {
user: state.user
}
}
const mapDispatchToProps = (dispatch) => {
return {
getUserData: () => dispatch(userActions.getUserData())
}
}
class Profile extends Component {
state:{
user: {}
}
componentDidMount (){
this.props.getUserData()
// when data is saved to redux state i need to save it to component state
}
editTextField = () => {
this.setState({
[e.target.id]: e.target.value
})
};
render(){
const { user } = this.props;
return(
<TextField id="firstName"
value={user.firstName}
onChange={this.editTextField}
/>
)
}
}
You can use componentDidUpdate for that or give a callback function to your action.
I will show both.
First lets see componentDidUpdate,
Here you can compare your previous data and your present data, and if there is some change, you can set your state, for example if you data is an array.
state = {
data: []
}
then inside your componentDidUpdate
componentDidUpdate(prevProps, prevState) {
if(prevProps.data.length !== this.props.data.length) {
// update your state, in your case you just need userData, so you
// can compare something like name or something else, but still
// for better equality check, you can use lodash, it will also check for objects,
this.setState({ data: this.props.data});
}
}
_.isEqual(a, b); // returns false if different
This was one solution, another solution is to pass a call back funtion to your action,
lets say you call this.props.getData()
you can do something like this
this.props.getData((data) => {
this.setState({ data });
})
here you pass your data from redux action to your state.
your redux action would be something like this.
export const getData = (done) => async dispatch => {
const data = await getSomeData(); // or api call
// when you dispatch your action, also call your done
done(data);
}
If you are using React 16.0+, you can use the static method getDerivedStateFromProps. You can read about it react docs.
Using your example:
class Profile extends Component {
// other methods here ...
static getDerivedStateFromProps(props) {
return {
user: props.user
}
}
// other methods here...
}
I am trying to follow this code in redux-saga
export const getUser = (state, login) => state.entities.users[login]
export const getRepo = (state, fullName) => state.entities.repos[fullName]
Which is then used in the saga like this:
import { getUser } from '../reducers/selectors'
// load user unless it is cached
function* loadUser(login, requiredFields) {
const user = yield select(getUser, login)
if (!user || requiredFields.some(key => !user.hasOwnProperty(key))) {
yield call(fetchUser, login)
}
}
This getUser reducer (is it even a reducer) looks very different from what I would normally expect a reducer to look like.
Can anyone explain what a selector is and how getUser is a reducer and how it fits in with redux-saga?
getUser is not a reducer, it is indeed a selector, that is, a function that knows how to extract a specific piece of data from the store.
Selectors provide an additional layer such that if you altered your store structure and all of a sudden your users were no longer at state.entities.users but instead at state.users.objects.entities (or whatever) then you only need to update the getUser selector and not every place in your app where you were making a reference to the old location.
That makes them particularly handy when it comes to refactoring your Redux store.
Selectors are getters for the redux state. Like getters, selectors encapsulate the structure of the state, and are reusable. Selectors can also compute derived properties.
You can write selectors, such as the ones you saw in redux-saga. For example:
const getUsersNumber = ({ users }) => users.length;
const getUsersIds = ({ users }) => users.map(({ id }) => id);
etc...
You can also use reselect, which is a simple “selector” library for Redux, that memoize selectors to make them more efficient.
Selectors are functions that take Redux state as an argument and return some data to pass to the component.
const getUserData = state => state.user.data;
Why should it be used?
One of the main reasons is to avoid duplicated data in Redux.
Your data object shape keeps varying as your application grows, so rather than making changes in all the related component.It is much recommended/easier to change the data at one place.
Selectors should be near reducers because they operate on the same state. It is easier for data to keep in sync.
Using reselect helps to memoize data meaning when the same input is passed to the function, returns the previous result rather than recalculating again.So, this enhances your application performance.
function mapStateToProps (state) {
return {
user: state.user,
}
}
initialState of reducer by user store
const initialState = {
isAdmin:false,
isAuth:false,
access:[1,2,5]
};
class AppComp extends React.Component{
render(){
const {user: { access:access}} = this.props;
const rand = Math.floor(Math.random()*4000)
return (<div>
{`APP ${rand} `}
<input type="button" defaultValue="change auth" onClick={this.onChangeUserAuth} />
<p>TOTAL STATUS COUNT IS {access.length}</p>
</div>)
}
}}
but you can use selector
var getUser = function(state) {
return state.user
}
const getAuthProp = createSelector(
getUser,
(user) => user.access
);
function mapStateToProps (state) {
return {
// user: state.user,
access: getAuthProp(state)
}
}
Main Problem is this component use all user: state.user and any changes in user (etc isAdmin ,isAuth, access) runs rerender this component which need only part of this store - access!!!
In Redux, whenever an action is called anywhere in the application,
all mounted & connected components call their mapStateToProps
function. This is why Reselect is awesome. It will just return the
memoized result if nothing has changed.
In the real world, you will most likely need the same certain part of
your state object in multiple components.
https://medium.com/#parkerdan/react-reselect-and-redux-b34017f8194c
The createSelector function provided by Reselect implements the most basic way to derive a selector from previous selectors. The simplest use case is to derive a selector from a single other selector. In this case, the parameters to createSelector are the input selector and a function transforming the result of that selector into the result of the new selector. For example
var getProducts = function(state) {
return state.products
}
import {getProducts} from '../app/selectors'
import {createSelector} from 'reselect'
export const getProductTitles = createSelector(
getProducts,
(products) => products.map((product) => product.get('title'))
)
This is equivalent to (ignoring memoization):
import {getProducts} from '../app/selectors'
export const getProductTitles = (state) => {
return getProducts(state).map((product) => product.get('title'))
}
The createSelector function can combine data from multiple selectors as well as from a single selector. We can pass any number of selectors to createSelector, and their results will be passed to the function passed as the final argument. For a (somewhat contrived) example:
const isInCheckout = createSelector(
getIsShippingPage,
getIsBillingPage,
getIsConfirmationPage,
(isShipping, isBilling, isConfirmation) =>
isShipping || isBilling || isConfirmation
)
is equivalent to
const isInCheckout = (state) => {
return (
getIsShippingPage(state) ||
getIsBilingPage(state) ||
getIsConfirmationPage(state)
)
}
common pattern when writing mapStateToProps functions with selectors is to return an object with each key storing the result of a particular selector. The createStructuredSelector helper function in Reselect lets us write this pattern with the minimum of boilerplate. For example, if we writ
const mapStateToProps = createStructuredSelector({
title: getProductTitle,
price: getProductPrice,
image: getProductImage
})
it is equivalent to
const mapStateToProps = (state) => {
return {
title: getProductTitle(state),
price: getProductPrice(state),
image: getProductImage(state)
}
}
https://docs.mobify.com/progressive-web/0.15.0/guides/reselect/