I'm writing a component (say ComponentUpdate) that modifies a list of items which is used by another component (say ComponentDisplay) that re-renders and display the new updated list.
So in that case, ComponentUpdate is using one of the dispatchers and have nothing to do with the store, it only dispatches a new list of items.
So to a dispatcher, I'm connecting the component with the dispatcher function I'm using react-redux connect method.
Is there any way of doing this without passing mapStateToProps in the connect function?
I'm connecting like -
const mapStateToProps = state => {
return state;
}
const mapDispatchToProps = dispatch => {
return ({
dispatchfunction: filters => dispatch(dispatchfunction(filters)),
})
}
connect(mapStateToProps, mapDispatchToProps)(ComponentUpdate)
I want something like this, but it is not working out:
const mapDispatchToProps = dispatch => {
return ({
dispatchfunction: filters => dispatch(dispatchfunction(filters)),
})
}
connect(mapDispatchToProps)(ComponentUpdate)
pass null instead of mapStateToProps
connect(null, mapDispatchToProps)(ComponentUpdate)
Or
You can import store from './redux' and call the action like, store.dispatchFunction(filters) anywhere you want. So you don't want to use connect function
Related
I'm using React and Redux in my web app.
In the login page, I have multiple fields (inputs).
The login page in composed from multiple components to pass the props to.
I was wondering how should I pass the props and update actions.
For example, lets assume I have 5 inputs in my login page.
LoginPage (container) -> AuthenticationForm (Component) -> SignupForm (Component)
In the LoginPage I map the state and dispatch to props,
and I see 2 options here:
mapStateToProps = (state) => ({
input1: state.input1,
...
input5: state.input5
})
mapDispatchToProps = (dispatch) => ({
changeInput1: (ev) => dispatch(updateInput1(ev.target.value))
...
changeInput5: (ev) => dispatch(updateInput5(ev.target.value))
})
In this solution, I need to pass a lot of props down the path (the dispatch actions and the state data).
Another way to do it is like this:
mapStateToProps = (state) => ({
values: {input1: state.input1, ..., input5: state.input5}
})
mapDispatchToProps = (dispatch) => ({
update: (name) => (ev) => dispatch(update(name, ev.target.value))
})
In this solution, I have to keep track and send the input name I want to update.
How should I engage this problem?
It seems like fundamental question, since a lot of forms have to handle it,
but I couldn't decide yet what would suit me now and for the long run.
What are the best practices?
I think best practice would be to handle all of this logic in the React component itself. You can use component's state to store input's data and use class methods to handle it. There is good explanation in React docs https://reactjs.org/docs/forms.html
You probably should pass data in Redux on submit. Ether storing whole state of the form as an object, or not store at all and just dispatching action with api call.
TL;DR. it's a more 'general' coding practice. But let's put it under a react-redux context.
Say if you go with your first approach, then you will probably have 5 actionCreators as:
function updateInput1({value}) { return {type: 'UPDATE_INPUT1', payload: {value}} }
...
function updateInput5({value}) { return {type: 'UPDATE_INPUT5', payload: {value}} }
Also if you have actionTypes, then:
const UPDATE_INPUT1 = 'UPDATE_INPUT1'
...
const UPDATE_INPUT5 = 'UPDATE_INPUT5'
The reducer will probably look like:
function handleInputUpdate(state = {}, {type, payload: {value}}) {
switch (type) {
case UPDATE_INPUT1: return {..., input1: value}
...
case UPDATE_INPUT5: return {..., input5: value}
default: return state
}
}
What's the problem? I don't think you're spreading too many props in mapStateToProps/mapDispatchToProps, Don't repeat yourself!
So naturally, you want a more generic function to avoid that:
const UPDATE_INPUT = 'UPDATE_INPUT'
function updateInput({name, value}) { return {type: UPDATE_INPUT, payload: {name, value}} }
function handleInputUpdate(state = {inputs: null}, {type, payload: {name, value}}) {
switch (type) {
case UPDATE_INPUT: return {inputs: {...state.inputs, [name]: value}}
default: return state
}
}
Finally, the "selector" part, based upon how the state was designed, get component's props from it would be fairly trivial:
function mapStateToProps(state) { return {inputs: state.inputs} }
function mapDispatchToProps(dispatch) { return {update(name, value) { dispatch(updateInput(name, value)) } }
In summary, it's not necessarily a redux/react problem, it's more how you design app state, redux just offers you utilities and poses some constraints to enable "time traveling" (state transitions are made explicit within a mutation handler based on a separate action).
Best practice to handle this problem is having a local state on your Form Component and managing it locally because I believe it's not a shared state. onSubmit you could dispatch your action passing down the state to the action which is required in making an API call or posting it to your server.
If you try to keep updating your store as the user types, it will keep dispatching the action which might cause problems in future. You read more here Handling multiple form inputs in react
I was reading the documentation for the Redux library and it has this example:
In addition to reading the state, container components can dispatch actions. In a similar fashion, you can define a function called mapDispatchToProps() that receives the dispatch() method and returns callback props that you want to inject into the presentational component.
This actually makes no sense. Why do you need mapDispatchToProps when you already have mapStateToProps?
They also provide this handy code sample:
const mapDispatchToProps = (dispatch) => {
return {
onTodoClick: (id) => {
dispatch(toggleTodo(id))
}
}
}
What is this function and why it is useful?
I feel like none of the answers have crystallized why mapDispatchToProps is useful.
This can really only be answered in the context of the container-component pattern, which I found best understood by first reading:Container Components then Usage with React.
In a nutshell, your components are supposed to be concerned only with displaying stuff. The only place they are supposed to get information from is their props.
Separated from "displaying stuff" (components) is:
how you get the stuff to display,
and how you handle events.
That is what containers are for.
Therefore, a "well designed" component in the pattern look like this:
class FancyAlerter extends Component {
sendAlert = () => {
this.props.sendTheAlert()
}
render() {
<div>
<h1>Today's Fancy Alert is {this.props.fancyInfo}</h1>
<Button onClick={sendAlert}/>
</div>
}
}
See how this component gets the info it displays from props (which came from the redux store via mapStateToProps) and it also gets its action function from its props: sendTheAlert().
That's where mapDispatchToProps comes in: in the corresponding container
// FancyButtonContainer.js
function mapDispatchToProps(dispatch) {
return({
sendTheAlert: () => {dispatch(ALERT_ACTION)}
})
}
function mapStateToProps(state) {
return({fancyInfo: "Fancy this:" + state.currentFunnyString})
}
export const FancyButtonContainer = connect(
mapStateToProps, mapDispatchToProps)(
FancyAlerter
)
I wonder if you can see, now that it's the container 1 that knows about redux and dispatch and store and state and ... stuff.
The component in the pattern, FancyAlerter, which does the rendering doesn't need to know about any of that stuff: it gets its method to call at onClick of the button, via its props.
And ... mapDispatchToProps was the useful means that redux provides to let the container easily pass that function into the wrapped component on its props.
All this looks very like the todo example in docs, and another answer here, but I have tried to cast it in the light of the pattern to emphasize why.
(Note: you can't use mapStateToProps for the same purpose as mapDispatchToProps for the basic reason that you don't have access to dispatch inside mapStateToProp. So you couldn't use mapStateToProps to give the wrapped component a method that uses dispatch.
I don't know why they chose to break it into two mapping functions - it might have been tidier to have mapToProps(state, dispatch, props) IE one function to do both!
1 Note that I deliberately explicitly named the container FancyButtonContainer, to highlight that it is a "thing" - the identity (and hence existence!) of the container as "a thing" is sometimes lost in the shorthand
export default connect(...)
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
syntax that is shown in most examples
It's basically a shorthand. So instead of having to write:
this.props.dispatch(toggleTodo(id));
You would use mapDispatchToProps as shown in your example code, and then elsewhere write:
this.props.onTodoClick(id);
or more likely in this case, you'd have that as the event handler:
<MyComponent onClick={this.props.onTodoClick} />
There's a helpful video by Dan Abramov on this here:
Redux: Generating Containers with connect() from React Redux (VisibleTodoList)
mapStateToProps() is a utility which helps your component get updated state(which is updated by some other components),
mapDispatchToProps() is a utility which will help your component to fire an action event (dispatching action which may cause change of application state)
mapStateToProps, mapDispatchToProps and connect from react-redux library provides a convenient way to access your state and dispatch function of your store. So basically connect is a higher order component, you can also think as a wrapper if this make sense for you. So every time your state is changed mapStateToProps will be called with your new state and subsequently as you props update component will run render function to render your component in browser. mapDispatchToProps also stores key-values on the props of your component, usually they take a form of a function. In such way you can trigger state change from your component onClick, onChange events.
From docs:
const TodoListComponent = ({ todos, onTodoClick }) => (
<ul>
{todos.map(todo =>
<Todo
key={todo.id}
{...todo}
onClick={() => onTodoClick(todo.id)}
/>
)}
</ul>
)
const mapStateToProps = (state) => {
return {
todos: getVisibleTodos(state.todos, state.visibilityFilter)
}
}
const mapDispatchToProps = (dispatch) => {
return {
onTodoClick: (id) => {
dispatch(toggleTodo(id))
}
}
}
function toggleTodo(index) {
return { type: TOGGLE_TODO, index }
}
const TodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
Also make sure that you are familiar with React stateless functions and Higher-Order Components
Now suppose there is an action for redux as:
export function addTodo(text) {
return {
type: ADD_TODO,
text
}
}
When you do import it,
import {addTodo} from './actions';
class Greeting extends React.Component {
handleOnClick = () => {
this.props.onTodoClick(); // This prop acts as key to callback prop for mapDispatchToProps
}
render() {
return <button onClick={this.handleOnClick}>Hello Redux</button>;
}
}
const mapDispatchToProps = dispatch => {
return {
onTodoClick: () => { // handles onTodoClick prop's call here
dispatch(addTodo())
}
}
}
export default connect(
null,
mapDispatchToProps
)(Greeting);
As function name says mapDispatchToProps(), map dispatch action to props(our component's props)
So prop onTodoClick is a key to mapDispatchToProps function which delegates furthere to dispatch action addTodo.
Also if you want to trim the code and bypass manual implementation, then you can do this,
import {addTodo} from './actions';
class Greeting extends React.Component {
handleOnClick = () => {
this.props.addTodo();
}
render() {
return <button onClick={this.handleOnClick}>Hello Redux</button>;
}
}
export default connect(
null,
{addTodo}
)(Greeting);
Which exactly means
const mapDispatchToProps = dispatch => {
return {
addTodo: () => {
dispatch(addTodo())
}
}
}
mapStateToProps receives the state and props and allows you to extract props from the state to pass to the component.
mapDispatchToProps receives dispatch and props and is meant for you to bind action creators to dispatch so when you execute the resulting function the action gets dispatched.
I find this only saves you from having to do dispatch(actionCreator()) within your component thus making it a bit easier to read.
React redux: connect: Arguments
I'm building a desktop app using React and Electron.
Since it's growing fast, I realized I need some kind of state management like Redux to avoid passing many properties between components.
I started reading Redux official documentation but still cannot figure out how to implement it in my case. I'm stuck!
For example, I have a main App component that renders many sub-components. One of them has a button. When clicked, it should dispatch an "event" to the store so the main App can act in consequence. How can I accomplish that?
I cannot find the concept of events and I've hit a wall on how to even start using Redux.
Why events? Because it seems silly to me to dispatch an action and modify app state in this case. I just want to inform the root component to dispatch an action based on a user action.
User interacts with a presentational component that should tell a container component to make an API call or start capturing audio/camera for example.
For what I know up to now, the only way to accomplish this is to mutate state so another component listening for changes detects a special value that means "hey, let's do this", then mutate state again to say "hey, I'm doing this", and when it's done state changes again with "hey, it's done".
Can someone point me in the right direction please?
User interacts with a presentational component that should tell a container component to make an API call or start capturing audio/camera for example.
Perhaps your container component is doing more than it should. Consider a situation where React components do no more than two things:
Display DOM elements based on props
Handle user input (dispatch events)
If you were not using redux and wanted to make an API call when clicking a button, that might look something like:
class App extends Component {
state = { data: {} }
makeAPICall() {
fetch(url).then(data => this.setState({ data }))
}
render() {
<Child
data={this.state.data}
makeAPICall={this.makeAPICall}
/>
}
}
let Child = ({ data, makeAPICall }) => (
<button onClick={makeAPICall}>Call API!</button>
)
The App component is responsible for storing global state and handling events, but we have to pass down that state and App's handlers through the component tree, quite possibly through components that will never themselves use those props.
By adding Redux your application now has a much better place to handle side effects like API calls or turning a camera on. Middleware!
Let this (crappy) illustration help you:
So now instead your App component can be just a normal presentational component like all of the others, simply displaying data based on store props and handling any user input / dispatching actions if need be. Let's update the above example using the thunk middleware
// actions.js
export let makeAPICall = () => {
return dispatch => {
fetch(url).then(data => dispatch({
type: 'API_SUCCESS',
payload: data,
})).catch(error => dispatch({ type: 'API_FAIL', payload: error }))
}
}
// Child.js
import { connect } from 'react-redux'
import { makeAPICall } from './actions'
let Child = ({ dispatch }) => (
<button onClick={() => dispatch(makeAPICall())}>Call API!</button>
)
export default connect()(Child)
Thinking about React applications this way is very powerful. The separation of concerns is very well laid out. Components display stuff and handle events. Middleware takes care of any side effects (if there need to be any) and the store simply is an object that will cause React to re-render in case its data changes.
UPDATE: "The Modal Problem"
React apps may have some global stuff like modals and tooltips. Don't think about the "open modal" event.. think "what is the current modal content?".
A modal setup may look something along these lines:
// modalReducer.js
function reducer (state = null, action) {
if (action.type === 'UPDATE_MODAL') {
return action.payload
}
// return default state
return state
}
// App.js
let App = connect(state => ({ modal: state.modal }))(
props =>
<div>
<OtherStuff />
<Modal component={props.modal} />
</div>
)
// Modal.js
let Modal = props =>
<div
style={{
position: 'fixed',
width: '100vw', height: '100vh',
opacity: props.component ? 1 : 0,
}}
>
{props.component}
</div>
// Child.js
let Child = connect()(props =>
<button onClick={e =>
dispatch({
type: 'UPDATE_MODAL'
payload: <YourAwesomeModal />
})
}>
Open your awesome modal!
</button>
)
This is just an example, but would work great! when state.modal is null your Modal has 0 opacity and won't show. When you dispatch UPDATE_MODAL and pass in a component, the modal will show whatever you dispatch and change the opacity to 1 so you can see it. Later you can dispatch { type: 'UPDATE_MODAL', payload: null } to close the modal.
Hopefully this gives you some things to think about!
Definitely read this answer by Dan. His approach is similar but stored modal "metadata" vs the component itself which lends itself better to Redux fanciness like time travel etc.
Is the reason you think it seems silly because you don't want your presentational components to be redux-aware? If so mapDispatchToProps and bindActionCreators might help tidy things up, for example:
// App.js
import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { someAction } from './actions';
import Button from './Button';
const App = ({ onButtonClick }) => (
<div>
Hello.
<Button onClick={onButtonClick}>Click me.</Button>
</div>
);
export default connect(null, dispatch => {
return bindActionCreators({
onButtonClick: someAction
}, dispatch);
})(App);
// Button.js
import React from 'react';
export default Button = ({ onClick, children }) => <button onClick={onClick}>{children}</button>;
As you can see only the connected container component is aware of the action, the Button (and even the App) are unaware that click triggers an action.
For what it's worth, I had a similar problem (click a button elsewhere in the tree and cause a map to reset its viewport) and solved it with a simple incremental key.
Button dispatches action:
export const RESET_MAP = "RESET_MAP";
export const resetMap = () => {
return {
type: RESET_MAP,
};
};
In reducer:
case RESET_MAP:
return Object.assign({}, state, {
setvar: state.setvar + 1
});
In map:
static getDerivedStateFromProps(newProps, state) {
var newState = null;
if (newProps.setvar !== state.setvar) {
newState = {
setvar: newProps.setvar,
[other magic to reset the viewport]
}
}
return newState;
I have this codepen, where store.subscribe() works and connect() doesn't work. Specifically, the component doesn't get updated with the new props. I suspected state mutation as I thought that connect()'s shallow equality check might be ignoring the change. But, I'm using Immutable.js for the state change in the reducer, and I also did my own ref check in my subscribe method and it is even shallowly different for every update. I feel like something obvious must be missing here...
Component:
class App extends React.Component{
...
}
const mapStateToProps = state => ({
alerts: state.alerts
});
const mapDispatchToProps = dispatch => ({
onAlert: (type, autoHide, message) =>
dispatch({ type: 'SHOW_ALERT', payload: { message, type, autoHide } })
});
const ConnectedApp = connect(mapStateToProps, mapDispatchToProps)(App);
Reducer:
const alertsReducer = (alerts=Immutable.List(), { type, payload }) => {
switch (type){
case 'SHOW_ALERT':
if (!payload.message || R.trim(payload.message).length === 0){
throw new Error('Message cannot be empty.');
}
return alerts.push(payload);
default:
return alerts;
}
};
Store:
const store = createStore(combineReducers({ alerts: alertsReducer }), applyMiddleware(ReduxThunk.default));
Render:
//** THIS DOESN'T WORK
// ReactDOM.render(<Provider store={store}><ConnectedApp /></Provider>, document.getElementById('main'));
//** THIS WORKS
store.subscribe(()=>{
render();
});
const render = () => {
ReactDOM.render(<App {...store.getState()} onAlert={
(type, autoHide, message) => store.dispatch({ type: 'SHOW_ALERT', payload: { message, type, autoHide } })
}/>, document.getElementById('main'));
};
render();
Is this because the top level state object still has the same reference? I tried removing Immutable.js and made the entire state the array with the reducer returning a new array every time. That still didn't work.
Versions:
react-redux#4.4.5
redux#3.5.2
react#15.3.1
if you open the console , there will be the error
addComponentAsRefTo(...): Only a ReactOwner can have refs. You might
be adding a ref to a component that was not created inside a
component's render method, or you have multiple copies of React
loaded
to solve it you should choose between https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react-with-addons.min.js
and
https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react.js, because React should be added to page once.
after that you need to add Provider from react-redux. Also you need to add key props in your lists.
changed pen http://codepen.io/anon/pen/dXLQLv?editors=0010
Let's say I've got an app with two reducers - tables and footer combined using combineReducers().
When I click on some button two actions are being dispatched - one after another: "REFRESH_TABLES" and "REFRESH_FOOTER".
tables reducer is listening for the first action and it modifies the state of tables. The second action triggers footer reducer. The thing is it needs current state of tables in order to do it's thing.
My implementation looks something like below.
Button component:
import React from 'react';
const refreshButton = React.createClass({
refresh () {
this.props.refreshTables();
this.props.refreshFooter(this.props.tables);
},
render() {
return (
<button onClick={this.refresh}>Refresh</button>
)
}
});
export default refreshButton;
ActionCreators:
export function refreshTables() {
return {
type: REFRESH_TABLES
}
}
export function refreshFooter(tables) {
return {
type: REFRESH_FOOTER,
tables
}
}
The problem is that the props didn't update at this point so the state of tables that footer reducer gets is also not updated yet and it contains the data form before the tables reducer run.
So how do I get a fresh state to the reducer when multiple actions are dispatched one after another from the view?
Seems you need to handle the actions async so you can use a custom middleware like redux-thuk to do something like this:
actions.js
function refreshTables() {
return {
type: REFRESH_TABLES
}
}
function refreshFooter(tables) {
return {
type: REFRESH_FOOTER,
tables
}
}
export function refresh() {
return function (dispatch, getState) {
dispatch(refreshTables())
.then(() => dispatch(refreshFooter(getState().tables)))
}
}
component
const refreshButton = React.createClass({
refresh () {
this.props.refresh();
},
{/* ... */}
});
Although splitting it asynchronous may help, the issue may be in the fact that you are using combineReducers. You should not have to rely on the tables from props, you want to use the source of truth which is state.
You need to look at rewriting the root reducer so you have access to all of state. I have done so by writing it like this.
const rootReducer = (state, action) => ({
tables: tableReducer(state.tables, action, state),
footer: footerReducer(state.footer, action, state)
});
With that you now have access to full state in both reducers so you shouldn't have to pass it around from props.
Your reducer could then looks like this.
const footerReducer = (state, action, { tables }) => {
...
};
That way you are not actually pulling in all parts of state as it starts to grow and only access what you need.