Reputation: 2132
so I finally understood Flux architecture with Redux (I connect every function with actions and call services from there)
Currently I have this problem:
State of my component:
this.state = {
username: '',
counter: '',
email: '',
buttonText: 'textbefore',
}
I have function in this component (where i dispatch action):
handleSubmit(e) {
e.preventDefault()
if(username && email) {
this.props.dispatch(eventActions.save(username, email))
}
else {
this.props.dispatch(alertActions.warn("Wystąpił błąd"))
this.props.dispatch(viewActions.hide())
}
}
This is action:
function save(username, email) {
return dispatch => {
eventService.signUp({username, email})
.then(
response => {
dispatch(alertActions.success("text))
},
error => {
dispatch(alertActions.error(error))
}
)
}
}
This is constants:
export const eventConstants = {
SIGNUP_REQUEST: 'EVENT_SIGNUP_REQUEST',
SIGNUP_SUCCESS: 'EVENT_SIGNUP_SUCCESS',
SIGNUP_FAILURE: 'EVENT_SIGNUP_FAILURE',
}
This is reducer:
import { eventConstants } from '../constants/event.constants'
export default function event(state={}, action){
switch(action.type){
case eventConstants.SIGNUP_REQUEST:
return{...state, ...{}}
case eventConstants.SIGNUP_SUCCESS:
return{...state, ...{}}
case eventConstants.SIGNUP_FAILURE:
return{...state, ...{}}
}
}
My questions are:
How after succesfully sending can I change the state of buttonText from 'textbefore' to 'textafter' ?
I have function, which resetForm, where can I call it from redux architecture?
resetForm = () => {
this.setState({
name: '',
message: '',
email: '',
buttonText: 'Wiadomość wysłana'
})
}
Upvotes: 0
Views: 1464
Reputation: 5112
The point of having Redux is having state managed OUTSIDE your component, in a STORE (where is it?).
An action must tell the store how to update the state and provide the necessary data to do it. An action must therefore contain at least a type (one of your constants) and the data necessary to update your state. A store is created with a reducer (which takes a state and an action and returns a state). I'm not sure what your application does, so this is based on what I understand.
Since a promise is used, I would suggest something along these lines:
function save(username, email) {
store => {
eventService.signUp({username, email})
.then(
response => {
store.dispatch( {
type: C.SUCCESS,
buttontext: 'textafter',
//plus potentially some other data
})
},
error => {
store.dispatch ( {
type: C.FAILED,
//plus potentially some other data
})
}
)
}
}
const signUp = save(username, email);
signUp(store); //store will dispatch the right action upon resolving or rejecting
and it is the reducer that will act upon the instructions given in the action object and return a new state.
const reducer = (state = {}, action) => {
switch (action.type) {
case C.SUCCESS:
return {
...state,
buttontext: action.buttontext
}
break;
case C.FAILED:
...
}
etc.
}
The store (see the docs):
import { createStore } from 'redux'
function reducer(state = {}, action) {
...
}
const initialState = { ... };
const store = createStore(reducer, /*initial state*/initialState)
store.dispatch({
...
})
You can pass down the store explicitly to your components (works for small apps, there are other ways, context, connect, etc.):
const render = () =>
ReactDOM.render(
<App store={store} /> //and then again pass store down to child components...
document.getElementById('react-container')
)
store.subscribe(render); //will be called each time an action is dispatched (updates the view)
To reset, say you have a button in your view component:
<button onClick={() => store.dispatch( reset() ) } //reset must return an action (action creator).
//In the reducer, you will have a case that acts upon this action and returns the new fresh state)
Wrapping up with React Redux
React Redux ease the complexity involved with implicitly passing the store via context.
First, we use a provider and wrap up the app component with it. Thus, the store will be added to the context and the component will be rendered each time actions are dispatched.
<Provider store={store}>
<App /> //that is your root component that contains other components
</Provider>
Then, we use connect to create container components. These are used when we want to decouple the store from UI components. Instead, the container will connect the UI component and the store. The UI components will not even know they're using a store at all!
connect expects two arguments, a mapStateToProps which injects state as an argument and returns an object that will be mapped to props.
For example, if your component expects a buttonText property:
const mapStateToProps = state => ({
buttonText: state.buttonText
})
And then a mapDispatchToProps, which injects the store's dispatch function as an argument which is then used in callback functions. Your component might expect callback functions as props, for example onSuccess:
const mapDispatchToProps = dispatch => ({
onSuccess() {
dispatch( reset() ) //the reset is the action creator, recall that it returns an action { type: ..., ... }
}
}).
So when your component raises onSuccess, the store will dispatch it.
Then we pass these functions to connect.
const MyContainerComponent = connect(
mapStateToProps,
mapDispatchToProps
)(MyPresentationalComponent) // the props are passed to MyPresentationalComponent
I hope this answers your question.
Upvotes: 2