I'm after a few of Redux tutorials, but still cannot understand how to implement this to my existing component. Every tutorial has a little different approach and I'm confused.
I have code like below and want to use Redux for state management. I'm assuming that I need to remove this.setState
functions and control state in reducer, but in this case how actually can I trigger function handleSubmit
? I wrote already reducer, store, dispatch
, but what I need more?
import React, { Component } from 'react';
import Dialog from 'material-ui/Dialog';
import FlatButton from 'material-ui/FlatButton';
import TextField from 'material-ui/TextField';
import { createStore } from "redux";
import { Provider } from 'react-redux'
const reducer = (state={errorText: '', open: true}, action) => {
switch(action.type) {
case "CHECK_PASSWORD": {
return state = {...state, open: false};
}
default:
return state;
}
}
const store = createStore(reducer, {
errorText: '',
open:true
});
store.subscribe(() => {
console.log('store change', store.getState())
})
store.dispatch({type: "CHECK_PASSWORD"})
export default class StartDialog extends Component {
handleSubmit = event => {
let password = this.refs.myPasswordValue.input.value;
// Check default password
if(password === "123"){
this.setState({ errorText: '', open: false})
} else if (password !== "123" && password !== "") {
this.setState({ errorText: "Password is incorrect" })
} else if (password === ""){
this.setState({ errorText: "Password is required" })
}
};
render() {
const actions = [ <FlatButton label="Submit" primary={true} onClick={this.handleSubmit} /> ];
return (
<Provider store={store} >
<Dialog title="Welcome to the React App!" actions={actions} modal={true} open={this.state.open} >
<TextField errorText={this.state.errorText}
hintText="Password Field"
floatingLabelText="Password"
type="password"
ref="myPasswordValue" />
</Dialog>
</Provider>
);}}
Actually you shouldn't adopt this type of structure as using <Provider>
in same component where actions are dispatched. Means I want you to think as parent-children relationship where Provider
would be in parent and all the store actions and subscriptions should be done in children. View Dan Abramov's discussion about having code structure for Redux as UI component should not have logic in it and logic should be controlled in parent component.
When you are using the state from store, then you don't need setState
. In fact when you need state as implicit data in some component, then you have to use setState
. Also while dispatching action, you have described type
property, but didn't attached actual payload(open
) as mentioned in reducer.
Also you can improve code structure as (In way of Novice Perspective) -
In index.js, the main <Provider>
Component should be integrated to supply state to below down the components as children.
Sample index.js
(Root) Code would be like -
import React from 'react';
import ReactDOM from 'react-dom';
import store from './store/store';
import TodoApp from './container/todoapp';
import { Provider } from 'react-redux';
const render = () => {
ReactDOM.render(
<Provider store={store}>
<TodoApp />
</Provider>
,document.getElementById('root'));
}
So now store is available to children components. So now you can use connect()
to connect to store directly as pass args
necessary as mapStateToProps
and mapDispatchToProps
.
Sample file of todoapp.js
from container folder -
import React from 'react';
import ReactDOM from 'react-dom';
import store from '../store/store';
import Todolist from './components/todolist';
import AddTodo from './components/addtodo';
import Footer from './components/footer';
import { connect } from 'react-redux';
var noteId = 0;
const getVisibleTodos = (todos,filter) => {
switch(filter){
case 'SHOW_ALL' :
return todos;
case 'SHOW_COMPLETED' :
return todos.filter(t => t.completed);
case 'SHOW_ACTIVE' :
return todos.filter(t => !t.completed);
default :
return todos;
}
}
class TodoApp extends React.Component{
render() {
const {todos,visibilityFilter} = this.props;
return(
<div>
<AddTodo onAddClick={this.props.onAddTodoClick} />
<ul>
<Todolist todos={this.props.todos}
onTodoClick ={this.props.onTodoListClick}/>
</ul>
<Footer visibilityFilter={this.props.visibilityFilter}
onFilterClick={this.props.onLinkClick} />
<button onClick = {() => console.log(store.getState())}> Arbitrary Button </button>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
visibilityFilter : state.visibilityFilter,
todos: getVisibleTodos(state.todos,state.visibilityFilter)
}
}
const mapDispatchToProps = (dispacth) => {
return {
onLinkClick : (filter) => {
store.dispatch({type :'SET_VISIBILITY_FILTER',filter});
},
onTodoListClick : (id) => {
store.dispatch({type:'TOGGLE_TODO',id});
},
onAddTodoClick : (text) => {
store.dispatch({type:'ADD_TODO',id : noteId++,text})
}
}
}
export default connect(mapStateToProps,mapDispatchToProps)(TodoApp);
render();
Store would consist of -
import {createStore, combineReducers} from 'redux';
const todo = (state = [], action) => {
switch (action.type) {
case 'ADD_TODO':
return {
id: action.id,
text: action.text,
completed: false
}
case 'TOGGLE_TODO':
if (state.id !== action.id) {
return state;
}
return {
...state,
completed: !state.completed
}
default:
return state;
}
}
const todos = (state = [], action) => {
switch (action.type) {
case 'ADD_TODO':
return [
...state,
todo(undefined, action)
]
case 'TOGGLE_TODO':
return state.map(t => todo(t, action));
default:
return state;
}
}
const visibilityFilter = (state = 'SHOW_ALL', action) => {
switch(action.type){
case 'SET_VISIBILITY_FILTER' :
return action.filter;
default :
return state;
}
}
const todoApp = combineReducers({ todos,visibilityFilter })
const store = createStore(todoApp);
console.log(store.getState());
export default store;
If you want to implement more complex project structure having to upscale current project then check this code structure - https://github.com/react-boilerplate/react-boilerplate