How to get the value as props in a different component - reactjs

In my react application, I have three parallel components. In my first component, I am doing an API call and based on the response I am routing the flow to Validated or Non-Validated Component.
Once the user is routed to validated component, there is a button on click of which user should be redirected to another component which should display the data in API response (first component) as key value pair. I am using Redux for state management.
The issue I am facing is the data is dispatched as an empty object from the store. I am not sure where I am going wrong but when I am debugging the app, I see the the action is not getting dispatched to the store and it's always returning me the initial state.
action.js-
export const setPoiData = (poiData) => dispatch => {
console.log('inside actions');
dispatch({
type: SET_POI_DATA,
payload: poiData
})
}
Reducer.js-
const initialState = {
poiData: {},
}
const reducerFunc = (state = initialState, action) => {
switch (action.type) {
case SET_POI_DATA:
console.log('inside poi reducers');
return {...state,poiData: action.payload}
default: return {...state}
}
}
Component 1-
//API call
Detail Component-
To get the data from store I am doing something like below-
componentDidMount() {
console.log(this.props.poiData)
}
function mapStateToProps(state) {
return {
poiData: state.poiData,
}
}
const mapDispatchToProps = dispatch => ({
setPoiData(data) {
dispatch(setPoiData(data));
}
})
I am not sure where I am going wrong. Can someone suggest me how to proceed ahead on this?

inside componentDidMount() you must call action like this this.props.setPoiData(<your data here>);

Related

React-Redux: how to set the state?

I am trying to understand someone else their code but have difficulty understand the interaction between Redux and React.
On a React page, I invoke a Redux action called getSubscriptionPlan. Inside that Redux action, I see it is able to load the correct data (point 1 below). This uses a reducer, in which I can again confirm the correct data is there (point 2 below).
Then the logic returns to the React page (point 3 below). I now would expect to be able to find somewhere in the Redux store the previously mentioned data. However, I can't find that data listed anywhere... not in this.state (where I would expect it), nor in this.props. Did the reducer perhaps not update the store state...?
What am I doing wrong and how can I get the data to point 3 below?
React page:
import { connect } from "react-redux";
import { getSubscriptionPlan } from "../../../appRedux/actions/planAction";
async componentDidMount() {
let { planId } = this.state;
await this.props.getSubscriptionPlan(planId);
// 3. I can't find the data anywhere here: not inside this.state and not inside this.props.
this.setState({plan: this.state.plan});
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.payment.paymentData !== this.props.payment.paymentData) {
this.setState({
checkout: this.props.payment.paymentData,
plan: this.props.payment.paymentData.plan,
});
}
}
const mapStateToProps = (state) => {
return {
plan: state.plan,
};
};
const mapDispatchToProps = (dispatch) => {
return bindActionCreators(
{ getSubscriptionPlan }, dispatch
);
};
export default withRouter(
connect(mapStateToProps, mapDispatchToProps)(Checkout)
);
Redux action:
export const getSubscriptionPlan = (id) => {
let token = getAuthToken();
return (dispatch) => {
axios
.get(`${url}/getSubscriptionPlan/${id}`, {
headers: { Authorization: `${token}` },
})
.then((res) => {
if (res.status === 200) {
// 1. From console.log(res.data) I know res.data correctly now contains the data
return dispatch({
type: GET_PLAN_SUCCESS,
payload: res.data,
});
})
};
};
Reducer:
export default function planReducer(state = initial_state, action) {
switch (action.type) {
case GET_PLAN_SUCCESS:
// 2. I know action.payload, at this point contains the correct data.
return { ...state, plan: action.payload };
default:
return state;
}
}
You are getting tripped up on how Redux works.
Redux does not use react component state. It manages state separately, and passes that state to components as props. When you call getSubscriptionPlan, you asynchronously dispatch an event to Redux, which handles the event and updates store state in the reducer. This state is the passed to the connected components mapStateToProps function, mapped to props, and then passed as props to your component. Passing new props triggers a componentDidUpdate and a rerender of the component.
A few key things here.
Redux does not interact with component state unless you explicitly set state with props passed from Redux.
Redux is asynchronous. That means that when you make a change to state via dispatch, the change is not immediately available in the component, but only available when new props are passed. It's event driven, not data binding. As a result, in your code you woun't see the plan prop in componentDidMount because at the time componentDidMount the call to getSubscriptionPlan hasn't happened.
You should see the prop populated in this.props in componentDidUpdate and in render before the didUpdate.
When working with react, it's best to think of components as basically functions of props with some extra lifecycle methods attached.

Reducer: getting information about value changes at the store

I don't understand how to get an updated value from the reducer store. For example, I have a React component. In this component after some actions, for example, after a few clicks on a button, I call the action from my reducer actions script like this.props.PostSomethingToServer(). Then the action sends some data to the node express server. The server makes some changes with data and then sends a response to the client store reducer. How can I get the updated data from the reducer store? I need to call another function in this React component with updated values.
By the way, I use mapStateToProps and export default connect() in the React component. As I know, mapStateToProps and export default connect() help to get data from the store before render(). But I still don't understand how to get updated data from the store after some actions.
A couple of code:
React component:
ChangeArrayData(){
let something = [];
// filling this array and modifying values
//then I call the action
this.props.postSomethingToServer(something);//something will be changed by server logic and will be returned with updated data by a response.
//then I wanna export the data to .excel, for example
this.ExportToExcel(); // here I have to get updated data from the reducer, but I don't have any information about changes in the reducer.
}
Reducer action:
export const postSomethingToServer= rankedElements => dispatch => {
axios
.post("/api/postData", elements)
.then(response => {
dispatch({
type: POST_SOMETHING_SUCCESSFUL,
status : "success",
payload: response.data
});
//... etc.
Reducer:
const initialState = {
something: {},
status: "",
error : ""
};
export default function(state = initialState, action) {
switch (action.type) {
case POST_SOMETHING:
return {
...state,
status: action.status,
}
case POST_SOMETHING_SUCCESSFUL:
return {
...state,
status: action.status,
something: action.payload
}
case GET_ERRORS:
return {
...state,
status: action.status,
error: action.error
}
default:
return state;
}
}
You Should assign reducer state values to some local state like following:
`const mapStateToProps = state => ({
contacts: state.data
});
export default connect(mapStateToProps, { actions })
(withStyles(contactStyle)(Contact));`
Here 'contacts' is a local state name we are using in the class and 'data' is a state name that we return from reducer after updating a state.
You can access the updated data using componentWillReceiveProps method like,
`componentWillReceiveProps(nextProps) {
if(nextProps.contacts !== undefined) {
//Handle updated states here
}
}`
After you dispatch the action, your reducer state something should have the data you expect. Given that you have mapped the data in your mapStateToProps function you can access it via props.
ChangeArrayData() {
let something = [];
this.props.postSomethingToServer(something);
this.ExportToExcel();
console.log(this.props.somethingReducerState);
}
const mapStateToProps = (state) => ({
somethingReducerState: state.yourReducerName.something,
});
I have the solution: I'm using componentWillReceiveProps(nextProps) and can receive a result from the reducer.
Thanks all for the answers.

How to save fetched data from server to component state using redux and redux-thunk?

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...
}

Is this Flux architecture?

This is how I've been organizing my React / Redux projects because it's how they did it in the tutorial I followed. Is this what Flux architecture is and if not what would you call this?
First I call a function in my component that's defined in the action file
This function does an ajax request to get info from an API
Then it fires off an action creator
The reducer listens for action creators and once one is detected it executes a function that updates the state
Here's an example:
Component
class List extends React.Component {
componentDidMount() {
this.props.getPosts();
}
// etc...
}
const mapStateToProps = state => {
return {
posts: state.posts
};
};
const mapDispatchToProps = dispatch => {
return {
getPosts: () => dispatch(actions.getPosts())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(List);
Action
const postsLoaded = posts => {
return {
type: actionTypes.POSTS_LOADED,
posts: posts
};
};
export const getPosts = () => {
return dispatch => {
axios
.get('http://api.something.com/posts', {})
.then(response => {
dispatch(postsLoaded(response.posts));
})
.catch(e => {
console.error(e);
});
};
};
Reducer
const setPosts = (prevState, action) => {
return {
...prevState,
...action.posts
};
};
const reducer = (prevState = {}, action) => {
switch (action.type) {
case actionTypes.POSTS_LOADED:
return setPosts(prevState, action);
default:
return prevState;
}
};
export default reducer;
Flux is a design pattern. Redux is one of several libraries that implement Flux. The intent is NOT for you to "use Redux to implement Flux", but rather "use the Flux pattern by using Redux".
You can find a much better description in the docs below, but in simplest terms, the Flux architecture is based on a unidirectional data flow, which means that each piece receives data from one place, and outputs changes to another. The intent of this pattern is to eliminate "spaghetti code", where various parts of the application pass data in many different directions, which can eventually become very difficult to trace.
In other words, your components are the "View" in the diagram below.
Redux store gives state to your component
Your component renders something, and when a user performs an action, the component creates an action and gives it to the dispatcher.
The dispatcher finds the reducer that can handle your action, and gives the result to the store.
And the cycle repeats.
This image and an in-depth overview of Flux can be found here.

Setting redux state doesn't work after a hard refresh

I am setting my redux state through a value I have in localStorage. This works fine when I navigate into my page. However, when I do a hard refresh the state is never set, despite the value in localStorage being passed down.
This is what my code looks like:
class SomeComponent {
componentWillMount() {
if (typeof localStorage !== 'undefined') {
console.log('I get to here...', localStorage.getItem('someValue')) // this comes in as expected always
this.props.setMyReduxState(localStorage.getItem('someValue'))
}
}
render () {
// will have the value of the localStorage item someValue when navigated into the page
// will be an empty string if I do a hard refresh
console.log('this.props.myReduxState', this.props.myReduxState)
return (
<div>
Stuff...
</div>
)
}
}
const mapStateToProps = (state) => {
return {
myReduxState: state.something.myReduxState || ''
}
}
const mapDispatchToProps = (dispatch) => {
return {
setMyReduxState (someValue) {
dispatch(setMyReduxState(someValue))
}
}
}
Any ideas?
Edit: just a small addition to simplify the problem: I also tried it sending a string directly to setMyReduxState function, without the localStorage, the state still isn't being set. So basically something like this:
componentWillMount() {
this.props.setMyReduxState('some string!')
}
From my understanding every time the redux state is set, the component should re-draw, which isn't happening when there is a hard refresh. Are there any reasons for this or something being done incorrectly?
Edit2: Including my action creator and reducer in case needed:
const SET_MY_REDUX_STRING = 'admin/users/SET_MY_REDUX_STRING'
const defaultState = {
myReduxState: ''
}
export function setMyReduxState (value) {
return {
type: SET_MY_REDUX_STRING,
myReduxState: value
}
}
export default function reducer (state = defaultState, action = {}) {
switch (action.type) {
case SET_MY_REDUX_STRING:
return Object.assign({}, state, { myReduxState: action.myReduxState })
default:
return state
}
}
Some of the checklist you need to follow while using redux -
Are you dispatching an action creator that returns an object with 'type' and data? Here 'type' is mandatory.
Does your reducer return the state with the updated data that it received?
Make sure you do not mutate the state in reducer. Always use {...state, someKey: someValue}

Resources