I am using redux for simple POC. I am passing props to component. However when I assign it to input box, the input box is not updating.
My component code:
CurrenctConverter.js
handleAmountchange(e) {
debugger;
var payload = e.target.value
store.dispatch({ type: "Add", val: payload })
}
render() {
return (
<div>
Currency Converter
USD <input type="text" onChange={this.debounceEventHandler(this.handleAmountchange, 1000)} value={this.props.globalstate.val}></input> **this inputbox not working**
INR <input type="text"></input>
<input type="button" value="Convert"></input>
</div>
)
}
redux store:
I am getting props from this store
import React from 'react'
import { createStore } from 'redux'
var initialstate = {
val: 100
}
const MyReducer = (state = initialstate, action) => {
if (action.type = "Add") {
return {
...state,
val: action.val
}
}
return state;
}
var mystore = createStore(MyReducer);
export default mystore;
In the reducer, please update the code as
var initialstate = {
val: 100
}
const MyReducer = (state = initialstate, action) => {
switch (action.type) {
case 'Add':
let updatedState={
...state,
val: action.val
}
return updatedState;
default:
return state;
}
var mystore = createStore(MyReducer);
export default mystore;
Related
I wonder what I am doing wrong here. The dispatch methods are dispatching correct values but the state object is showing wrong values.
{ name: "name", room: "room" } is what I am dispatching separately. But the state is showing { name: "room": room: "" }
Google chrome logs :
NOTE: please checkout the code here from the github repo incase needed.
Reducer:
export const initialState = {
name: '',
room: ''
}
export const reducer = (state, action) => {
console.log("Calling action", action);
switch (action.type) {
case types.SET_NAME:
return { ...state, name: action.name }
case types.SET_ROOM:
return { ...state, name: action.room }
default:
return state;
}
}
_app component:
import DataProvider from "../context/DataProvider";
import { initialState } from '../reducers/index';
import { reducer } from '../reducers';
const AppComponent = ({ Component, pageProps }) => {
return (
<DataProvider intialState={initialState} reducer={reducer}>
<Component {...pageProps} />
</DataProvider>
)
}
AppComponent.getInitialProps = async (appContext) => {
let pageProps = {};
if (appContext.Component.getInitialProps) {
pageProps = await appContext.Component.getInitialProps(appContext.ctx);
}
return { pageProps }
}
export default AppComponent;
Component:
const Join = () => {
const [name, setName] = input('');
const [room, setRoom] = input('');
const [state, dispatch] = useContext(DataContext);
const submit = (e) => {
if (name === '' || room === '') {
e.preventDefault();
return;
}
dispatch({
type: types.SET_NAME,
name
});
dispatch({
type: types.SET_ROOM,
room
});
}
return (
<div>
<h1>Join</h1>
<input onChange={(e) => setName(e)} placeholder="name" />
<input onChange={(e) => setRoom(e)} placeholder="room" />
<Link href="/chat">
<button type="submit" onClick={(e) => submit(e)}>Submit</button>
</Link>
</div>
)
}
Chat component (where I am consuming state):
const Chat = () => {
// const backendEndpoint = 'http://localhost:5000';
const [state, dispatch] = useContext(DataContext);
console.log('STATE', state)
return <h1>Chat</h1>
}
Chat.getInitialProps = async (ctx) => {
return {}
}
export default Chat;
I think the problem is in your reducer
case types.SET_ROOM:
return { ...state, name: action.room }
Here you change the name in rooms action
maybe you need to update like this
return { ...state, room: action.room }
actually u make a mistake in your Reducer.js
export const reducer = (state, action) => {
console.log("Calling action", action);
switch (action.type) {
case types.SET_NAME:
// equals state.name = action.name
// state = { name: 'name', room: '' }
return { ...state, name: action.name }
case types.SET_ROOM:
// equal state.name = action.room
// state = { name: 'room', room: '' }
return { ...state, name: action.room }
default:
return state;
}
}
// u can change your code style to reduce mistakes
export const reducer = (state, action) => {
const {name, room} = action
switch (action.type) {
case types.SET_NAME:
return { ...state, name }
case types.SET_ROOM:
return { ...state, room }
default:
return state;
}
}
i have this code from https://jsbin.com/zirugiteju/edit?js,console,output
but i have a question, what is {store.getState().todos} its in the bottom of the code, see please this part
const render = () => {
ReactDOM.render(
this is the code complete
const todo = (state, action) => {
switch(action.type) {
case 'ADD_TODO':
console.log("ADD");
return {
id : action.id,
text: action.text,
completed: false
};
break;
case 'TOGGLE_TODO':
if (state.id !== action.id ) {
return state;
}
return {
...state,
completed: !state.completed
};
break;
default:
return state;
}
};
const todos = (state=[], action) => {
switch(action.type) {
case 'ADD_TODO':
return [
...state,
todo(undefined, action)
];
break;
case 'TOGGLE_TODO':
return state.map(t =>todo(t, action));
break;
default:
return state;
}
};
const visibilityFilter = (state='SHOW_ALL', action) => {
switch (action.type) {
case 'SET_VISIBILITY_FILTER':
return action.filter;
break;
default:
return state;
}
};
const { combineReducers } = Redux;
const todoApp = combineReducers({
todos,
visibilityFilter
});
const { createStore } = Redux;
const store = createStore(todoApp);
const { Component } = React;
let nextTodoId = 0;
class TodoApp extends Component {
render() {
return (
<div>
<input ref={node => {
this.input = node
}} />
<button onClick={()=>{
store.dispatch({
type: 'ADD_TODO',
text: this.input.value,
id : nextTodoId++
});
this.input.value = '';
}}>
Add Todo</button>
<ul>
{this.props.todos.map(todo =>
<li key={todo.id}>
{todo.text}
</li>
)}
</ul>
</div>
);
}
}
const render = () => {
ReactDOM.render(
<TodoApp
todos={store.getState().todos}
/>,
document.getElementById('root')
);
};
store.subscribe(render);
render();
The prop todos = {store.getState().todos} that you are passing to TodoApp component is a reducer, called todos, from your redux state that you combined in this part of the code:
const todoApp = combineReducers({
todos,
visibilityFilter
});
The getState() function returns the current state tree of your application. It is equal to the last value returned by the store's reducer.
You can learn more about redux States in this link: https://redux.js.org/api/store
I've been trying to implement unit testing with react using the react-testing-library
I want to test my login component that use useSelector and useDispatch hooks from react-redux
The problem is that when I use this function to pass the store to in my login.test.js is not recognizing the reducer and show me this error:
An error occurred while selecting the store state: Cannot read property 'isLoading' of undefined.
const isLoadingAuth = useSelector(state => state.Auth.isLoading);
I use combineReducers in my store (the app has a lot of reducers) in order to access in that specific reducer "Auth" but I don't know how to use them in my login.test.js
How can I access to my Auth reducer in my login.test.js file?
This is my login.jsx
const LoginForm = () => {
const [values, setValues] = useState({ email: "", password: "" });
const dispatch = useDispatch();
function handleChange(e) {
const { name, value } = e.target;
setValues({ ...values, [name]: value });
}
function submitData(e) {
e.preventDefault();
dispatch(actions.AuthUser(values));
}
const isLoadingAuth = useSelector(state => state.Auth.isLoading);
const error = useSelector(state => state.Auth.err);
const isAuthSucess = useSelector(state => state.Auth.isAuthSuccess);
if (isAuthSuccess) {
<Redirect to="/dashboard" />;
}
return (
<>
<div>
<form onSubmit={submitData}>
<Input
label="Email"
name="email"
value={values.email}
change={handleChange}
/>
<Input
label="Password"
name="password"
type="password"
value={values.password}
change={handleChange}
/>
<div>
<button>Entrar</button>
</div>
</form>
</div>
</>
);
};
My AuthReducer.js
import * as actionTypes from "../actions/Auth/types";
import { updateObject } from "../store/utility";
export const initalState = {
authData: null,
isLoading: false,
isAuthSuccess: null,
err: null
};
const authStart = state => {
return updateObject(state, {
isLoading: true,
err: null
});
};
const authFail = (state, action) => {
return updateObject(state, {
isLoading: false,
err: action.err
});
};
const auth = (state, action) => {
return updateObject(state, {
isLoading: false,
authData: action.authData,
isAuthSuccess: true
});
};
export function reducer(state = initalState, action) {
switch (action.type) {
case actionTypes.START_AUTH_REQ: {
return authStart(state, action);
}
case actionTypes.FAIL_AUTH_REQ: {
return authFail(state, action);
}
case actionTypes.AUTH: {
return auth(state, action);
}
default:
return state;
}
}
export default reducer;
And my Login.test.js
import React from "react";
import { createStore, combineReducers } from "redux";
import { Provider } from "react-redux";
import { render, cleanup, fireEvent } from "#testing-library/react";
import rootReducer from "../../../../reducers";
import "#testing-library/jest-dom/extend-expect";
import LoginForm from "./Form";
function renderWithRedux(
ui,
{
initialState,
store = createStore(combineReducers(rootReducer, initialState))
} = {}
) {
return {
...render(<Provider store={store}>{ui}</Provider>),
// adding `store` to the returned utilities to allow us
// to reference it in our tests (just try to avoid using
// this to test implementation details).
store
};
}
test("can render with redux with custom initial state", () => {
const { getByTestId, getByText } = renderWithRedux(<LoginForm />, {
initialState: { isLoading: false }
});
});
Your initial state is for you entire store so needs to match the structure of your root reducer:
test("can render with redux with custom initial state", () => {
const { getByTestId, getByText } = renderWithRedux(<LoginForm />, {
initialState: { Auth: { isLoading: false } }
});
});
I know it is a late reply but might help someone.
The problem with the above code is that it is using combineReducer correctly but passing state of AuthReducer only.
The combineReducer is expecting a consolidated state. For example:
const state = {
auth: initialState,
app: {
temp: {
// Some state
}
}
}
function renderWithRedux(ui: any, state: any) {
const store = createStore(rootReducer, state)
return {
...render(<Provider store={ store } > { ui } < /Provider>),
store,
}
}
test('can render with redux with custom initial state', () => {
const { getByTestId, getByText } = renderWithRedux(<LoginForm />, {
...state,
auth: {
...initialState, loading: true
}
});
});
I have three tabs in my application.I used to get values from each and save the data in the third tab.The application works fine if the navigation order is not changed.(i.e)Tab1-->Tab2-->Tab3.
But if when I navigate from Tab3-->Tab2-->Tab3 .The value from the Tab1 gets null.
similarly when I navigate from Tab3-->Tab1-->Tab3 .The value from the Tab2 gets null.
Reducer.js
const initialState = {
external: [],
internal: [],
usercode:'',
vehicleImage:'',
checkInoutcontrols:[]
}
const Reducer = (state = initialState, action) => {
switch (action.type) {
case 'insertExternalCoordinates':
return { external: action.value }
case 'insertInternalCoordinates':
return { internal: action.value }
case 'insertUserCode':
return {usercode:action.value}
case 'insertImage':
return {vehicleImage:action.value}
case 'insertCheckInOutControls':
return {checkInoutcontrols:action.value}
}
return state;
}
export default Reducer
Tab1
//Saving state ---redux
const mapStateToProps = state => ({
external: state.external
})
//inserting values using function --- redux
const mapDispatchToProps = dispatch => ({
insertExternalCoordinates: (value) => dispatch({ type:
'insertExternalCoordinates', value: value })
});
export default connect(mapStateToProps, mapDispatchToProps)
(CheckOutExternal)
Tab2
//Saving state ---redux
const mapStateToProps = state => ({
insertCheckInOutControls: state.insertCheckInOutControls
})
//inserting values using function --- redux
const mapDispatchToProps = dispatch => ({
insertCheckInOutControls: (value) => dispatch({ type:
'insertCheckInOutControls', value: value })
});
export default connect(mapStateToProps, mapDispatchToProps)
(CheckOutParts)
Tab3
//Saving state ---redux
const mapStateToProps = state => ({
insertCheckInOutControls: state.insertCheckInOutControls
external:state.external,
usercode: state.usercode,
checkInoutcontrols:state.checkInoutcontrols
})
//inserting values using function --- redux
const mapDispatchToProps = dispatch => ({
insertExternalCoordinates: (value) => dispatch({ type:
'insertExternalCoordinates', value: value }),
insertCheckInOutControls: (value) => dispatch({ type:
'insertCheckInOutControls', value: value })
});
export default connect(mapStateToProps, mapDispatchToProps)
(CheckOutSignature)
Apps.js -----store is created
import React, {Component} from 'react';
import {KeyboardAvoidingView} from 'react-native';
import AppNavigation from './main';
import Reducer from './modules/Reducers';
import {Provider} from 'react-redux'
import {createStore} from 'redux';
const store = createStore(Reducer)
const App = () => ({
render() {
return (
<Provider store={store}>
<AppNavigation/>
</Provider>
);
}
})
export default App;
Can anyone help me to solve this.
It seems issue is in the reducer, you are only returning the updated key-value pair instead of full reducer state. So after each update reducer will have only one key-value pair, the last updated one. Add ...state to each object you are returning, it will keep the other properties.
Write you reducer like this:
const Reducer = (state = initialState, action) => {
switch (action.type) {
case 'insertExternalCoordinates':
return { ...state, external: action.value }
case 'insertInternalCoordinates':
return { ...state,, internal: action.value }
case 'insertUserCode':
return { ...state,, usercode:action.value }
case 'insertImage':
return { ...state, vehicleImage:action.value }
case 'insertCheckInOutControls':
return { ...state, checkInoutcontrols:action.value }
}
return state;
}
Check this example for more details:
let obj = { a:1, b: 2 };
function update(key, value) {
switch(key) {
case 'a': return { ...obj, a: value }
case 'b': return { ...obj, b: value }
}
return obj;
}
let newObj = update('a', 10);
console.log('obj', obj);
console.log('newObj', newObj);
I am trying to update state each time I type into an input. The action creator is firing but the reducer is not and I am not sure why. Below is the component and the reducer file. I have commented where the console.log works and where is does not. Some suggestions are greatly appreciated, thanks in advance.
//Component
import React, { Component } from "react";
import { Link, Route } from "react-router-dom";
import { connect } from "react-redux";
import {
updateName,
updateAddress,
updateCity,
updateState,
updateZip
} from "../../redux/reducers/reducer";
class StepOne extends Component {
render() {
return (
<div>
<div className="name">
<h3>Property Name</h3>
<input
name="name"
placeholder="Name"
onChange={e => updateName(e.target.value)}
/>
</div>
<div className="address">
<h3>Address</h3>
<input
name="address"
placeholder="Address"
onChange={e => updateAddress(e.target.value)}
/>
</div>
<div className="city">
<h3>City</h3>
<input
name="city"
placeholder="City"
onChange={e => updateCity(e.target.value)}
/>
</div>
<div className="state">
<h3>State</h3>
<input
name="state"
placeholder="State"
onChange={e => updateState(e.target.value)}
/>
</div>
<div className="zip">
<h3>Zip</h3>
<input
name="zip"
placeholder="Zip"
onChange={e => updateZip(e.target.value)}
/>
</div>
<Link to="/wizard/step_two">
<button onClick={console.log(this.props)}>Next Step</button>
</Link>
</div>
);
}
}
const mapStateToProps = state => {
return {
state
};
};
export default connect(
mapStateToProps,
{
updateName,
updateAddress,
updateCity,
updateState,
updateZip
}
)(StepOne);
//Reducer
const initialState = {
name: "",
address: "",
city: "",
state: "",
zip: 0,
image: "",
monthly_mortgage: 0,
desired_rent: 0,
houses: []
};
const NAME_INPUT = "NAME_INPUT";
const ADDRESS_INPUT = "ADDRESS";
const CITY_INPUT = "CITY_INPUT";
const STATE_INPUT = "STATE_INPUT";
const ZIP_INPUT = "ZIP_INPUT";
const IMAGE_INPUT = "IMAGE_INPUT";
const MORTGAGE_INPUT = "MORTGAGE_INPUT";
const DESIRED_RENT_INPUT = "DESIRED_RENT_INPUT";
function reducer(state = initialState, action) {
console.log("REDUCER HIT: Action ->", action); // console.log does not work
switch (action.type) {
case NAME_INPUT:
return { ...state, name: action.payload };
case ADDRESS_INPUT:
return { ...state, address: action.payload };
case CITY_INPUT:
return { ...state, city: action.payload };
case STATE_INPUT:
return { ...state, state: action.payload };
case ZIP_INPUT:
return { ...state, zip: action.payload };
case IMAGE_INPUT:
return { ...state, image: action.payload };
case MORTGAGE_INPUT:
return { ...state, monthly_mortgage: action.payload };
case DESIRED_RENT_INPUT:
return { ...state, desired_rent: action.payload };
default:
return state;
}
}
export function updateName(name) {
console.log(name); // console.log is working
return {
type: NAME_INPUT,
payload: name
};
}
export function updateAddress(address) {
return {
type: ADDRESS_INPUT,
payload: address
};
}
export function updateCity(city) {
return {
type: CITY_INPUT,
payload: city
};
}
export function updateState(state) {
return {
type: STATE_INPUT,
payload: state
};
}
export function updateZip(zip) {
return {
type: ZIP_INPUT,
payload: zip
};
}
export default reducer;
//Store
import { createStore } from "redux";
import reducer from "./reducers/reducer";
export default createStore(reducer);
You are using your action creators directly from import, but for them to have the full redux flow, you need to use them from this.props e.g:
this.props.updateName()
The second parameter of the connect() function maps dispatch to your action creators, so your actions can be dispatched to the root reducer.