Alert appearing twice in react - javascript

Why this alert box is appearing twice in a simple useReducer Application,
Click here
for the codesandbox link,
To regenerate the scenario
increase the value of input using up arrow (Press only once)
decrease the value of input by down key (Press only once)
here is the code
import "./styles.css";
import { useReducer } from "react";
const reducerFunction = (state, action) => {
switch (action.type) {
case "testing": {
if (action.payload.value > 0) {
return { ...state, testing: action.payload.value };
} else {
alert("some message");
return state;
}
}
default: {
throw new Error("Invalid Action");
}
}
};
export default function App() {
const defaultValue = {
testing: 0
};
const [state, dispatch] = useReducer(reducerFunction, defaultValue);
return (
<input
value={state.testing}
onChange={(e) => {
dispatch({ type: "testing", payload: { value: e.target.value } });
}}
type="number"
/>
);
}

You are using StrictMode which invokes functions passed to useReducer twice, from docs:
Strict mode can’t automatically detect side effects for you, but it
can help you spot them by making them a little more deterministic.
This is done by intentionally double-invoking the following functions:
Functions passed to useState, useMemo, or useReducer
// Remove Strict.Mode
ReactDOM.render(
<App />,
rootElement
);

Your initial state is this:
const defaultValue = {
testing: 0
};
Which is bound to the input value, but then from your reducer you're returning:
return { inputValue: action.payload.value };
Causing testing to be undefined.
You can see this error in console:
Warning: A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://reactjs.org/link/controlled-components
Your code should be
return {...state, testing: action.payload.value };
This will show one alert and prevent negative values

Dennis is correct about the strict mode behavior which is causing the issue here.
If you still need to use strict mode in the project you can use the code below to update the same-
Code Sandbox Link

Related

React/Redux component with checkboxes does not update when click on checkbox even though I'm returning new state

I've been stuck for a while trying to make the re-render in the checkboxes to work, the variables are being updated but it's just the rendering that doesn't happen.
I'm receiving a response from the backend that contains an object with an array of steps, I'm going to render a checkbox for every step if it's from a specific type. As soon as I received the object, I add in every step a new property value to use it later for checking the checkboxes.
This is my reducer:
export const MyObject = (state: MyObject = defaultState, action: FetchMyObjectAction | UpdateStepsInMyObjectAction) => {
switch (action.type) {
case "FETCH_MYOBJECT":
return {
...action.payload, // MyObject
steps: action.payload.steps.map((step) => {
if (step.control.controlType === "1") { // "1" = checkbox
return {
...step,
value: step.control.defaultValues[0] === "true" ? true : false, // Adding the property value
};
}
return step;
}),
};
case "UPDATE_STEPS":
return {
...state,
steps: state.steps.map((step) => {
if (step.id === action.payload.stepId) { // if this is the checkbox to update
return {
...step,
value: action.payload.checked,
};
}
return step;
}),
};
default:
return state;
}
This is how I'm rendering the checkboxes:
for (let step of steps) {
if (step.control.controlType === "1") {
controls.push(
<Checkbox
label={step.displayName}
checked={step.value}
onChange={(_ev, checked) => {
callback(step.id, checked);
}}
disabled={false}
className={classNames.checkbox}
/>
);
}
}
callback is a function that calls the reducer above for the case "UPDATE_STEPS".
After inspecting the variables I can see that they are being updated properly, it's just that the re-render doesn't happen in the checkboxes, not even the first time I check the box, the check doesn't appear. If I move to a different component and then go back to the component with the checkboxes I can see now the checks. But if I check/uncheck within the same component, nothing happens visually.
As far as I know, I'm returning new objects for every update, so mutability is not happening. Can you see what I'm missing?
Thanks!
First I would inspect if the checkbox works with useState to manage your state.
import { useState } from "react";
function CheckBoxForm() {
const [checked, setChecked] = useState(false);
return <Checkbox checked={checked} onChange={() => setChecked(!checked)} />;
}
Then I would check if you have wired up the reducer correctly using redux or useReducer. When you dispatch an action it should trigger a rerender. For troubleshooting this using redux please refer to the redux docs: https://react-redux.js.org/troubleshooting#my-views-aren-t-updating-when-something-changes-outside-of-redux.
You may be updating the object directly rather than dispatching an action using the function provided by the redux store. If you are using hooks, here is how you wire up your app to make sure the component props are subscribed to changing in the redux store. You must wrap with a provider, use redux hooks like useSelector and use their provided dispatch function: https://react-redux.js.org/api/hooks
Using useReducer is a much simpler process and will be easier to troubleshoot: https://beta.reactjs.org/reference/react/useReducer

useEffect running two times, but my dependencies array is empty, why is this happening? [duplicate]

I am new to reactJS and am writing code so that before the data is loaded from DB, it will show loading message, and then after it is loaded, render components with the loaded data. To do this, I am using both useState hook and useEffect hook. Here is the code:
The problem is, useEffect is triggered twice when I check with console.log. The code is thus querying the same data twice, which should be avoided.
Below is the code that I wrote:
import React from 'react';
import './App.css';
import {useState,useEffect} from 'react';
import Postspreview from '../components/Postspreview'
const indexarray=[]; //The array to which the fetched data will be pushed
function Home() {
const [isLoading,setLoad]=useState(true);
useEffect(()=>{
/*
Query logic to query from DB and push to indexarray
*/
setLoad(false); // To indicate that the loading is complete
})
},[]);
if (isLoading===true){
console.log("Loading");
return <div>This is loading...</div>
}
else {
console.log("Loaded!"); //This is actually logged twice.
return (
<div>
<div className="posts_preview_columns">
{indexarray.map(indexarray=>
<Postspreview
username={indexarray.username}
idThumbnail={indexarray.profile_thumbnail}
nickname={indexarray.nickname}
postThumbnail={indexarray.photolink}
/>
)}
</div>
</div>
);
}
}
export default Home;
Can someone help me out in understanding why it is called twice, and how to fix the code properly?
Thank you very much!
Put the console.log inside the useEffect
Probably you have other side effects that cause the component to rerender but the useEffect itself will only be called once. You can see this for sure with the following code.
useEffect(()=>{
/*
Query logic
*/
console.log('i fire once');
},[]);
If the log "i fire once" is triggered more than once it means your issue is
one of 3 things.
This component appears more than once in your page
This one should be obvious, your component is in the page a couple of times and each one will mount and run the useEffect
Something higher up the tree is unmounting and remounting
The component is being forced to unmount and remount on its initial render. This could be something like a "key" change happening higher up the tree. you need to go up each level with this useEffect until it renders only once. then you should be able to find the cause or the remount.
React.Strict mode is on
StrictMode renders components twice (on dev but not production) in order to detect any problems with your code and warn you about them (which can be quite useful).
This answer was pointed out by #johnhendirx and written by #rangfu, see link and give him some love if this was your problem. If you're having issues because of this it usually means you're not using useEffect for its intended purpose. There's some great information about this in the beta docs you can read that here
Remove <React.StrictMode> from index.js
This code will be
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
this
root.render(
<App />
);
React StrictMode renders components twice on dev server
You are most likely checking the issue on a dev environment with strict mode enabled.
To validate this is the case, search for <React.StrictMode> tag and remove it, or build for production. The double render issue should be gone.
From React official documentation
Strict mode can’t automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following functions:
Functions passed to useState, useMemo, or useReducer
[...]
Strict Mode - Reactjs docs
Similar question here My React Component is rendering twice because of Strict Mode
Please check your index.js
<React.StrictMode>
<App />
</React.StrictMode>
Remove the <React.StrictMode> wrapper
you should now fire once
root.render(
<App />
);
react root > index.js > remove <React.StrictMode> wrapper
It is the feature of ReactJS while we use React.StrictMode. StrictMode activates additional checks and warnings for its descendants nodes. Because app should not crash in case of any bad practice in code. We can say StrictMode is a safety check to verify the component twice to detect an error.
You will get this <React.StricyMode> at root of the component.
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
if you want to restrict components to render twice, You can remove <React.StrictMode> and check it. But It is necessary to use StrictMode to detect a run time error in case of bad code practice.
if you are using Next js, change reactStrictMode from "true" to false :
add this to your next.config.js
reactStrictMode: false,
I have found a very good explanation behind twice component mounting in React 18. UseEffect called twice in React
Note: In production, it works fine. Under strict mode in the development environment, twice mounting is intentionally added to handle the errors and required cleanups.
I'm using this as my alternative useFocusEffect. I used nested react navigation stacks like tabs and drawers and refactoring using useEffect doesn't work on me as expected.
import React, { useEffect, useState } from 'react'
import { useFocusEffect } from '#react-navigation/native'
const app = () = {
const [isloaded, setLoaded] = useState(false)
useFocusEffect(() => {
if (!isloaded) {
console.log('This should called once')
setLoaded(true)
}
return () => {}
}, [])
}
Also, there's an instance that you navigating twice on the screen.
Not sure why you won't put the result in state, here is an example that calls the effect once so you must have done something in code not posted that makes it render again:
const App = () => {
const [isLoading, setLoad] = React.useState(true)
const [data, setData] = React.useState([])
React.useEffect(() => {
console.log('in effect')
fetch('https://jsonplaceholder.typicode.com/todos')
.then(result => result.json())
.then(data => {
setLoad(false)//causes re render
setData(data)//causes re render
})
},[])
//first log in console, effect happens after render
console.log('rendering:', data.length, isLoading)
return <pre>{JSON.stringify(data, undefined, 2)}</pre>
}
//render app
ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
To prevent the extra render you can combine data and loading in one state:
const useIsMounted = () => {
const isMounted = React.useRef(false);
React.useEffect(() => {
isMounted.current = true;
return () => isMounted.current = false;
}, []);
return isMounted;
};
const App = () => {
const [result, setResult] = React.useState({
loading: true,
data: []
})
const isMounted = useIsMounted();
React.useEffect(() => {
console.log('in effect')
fetch('https://jsonplaceholder.typicode.com/todos')
.then(result => result.json())
.then(data => {
//before setting state in async function you should
// alsways check if the component is still mounted or
// react will spit out warnings
isMounted.current && setResult({ loading: false, data })
})
},[isMounted])
console.log(
'rendering:',
result.data.length,
result.loading
)
return (
<pre>{JSON.stringify(result.data, undefined, 2)}</pre>
)
}
//render app
ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
The new React docs (currently in beta) have a section describing precisely this behavior:
How to handle the Effect firing twice in development
From the docs:
Usually, the answer is to implement the cleanup function. The cleanup function should stop or undo whatever the Effect was doing. The rule of thumb is that the user shouldn’t be able to distinguish between the Effect running once (as in production) and a setup → cleanup → setup sequence (as you’d see in development).
So this warning should make you double check your useEffect, usually means you need to implement a cleanup function.
This may not be the ideal solution. But I used a workaround.
var ranonce = false;
useEffect(() => {
if (!ranonce) {
//Run you code
ranonce = true
}
}, [])
Even though useEffect runs twice code that matters only run once.
As others have already pointed out, this happens most likely due to a Strict Mode feature introduced in React 18.0.
I wrote a blog post that explains why this is happening and what you can do to work around it.
But if you just want to see the code, here you go:
let initialized = false
useEffect(() => {
if (!initialized) {
initialized = true
// My actual effect logic...
...
}
}, [])
Or as a re-usable hook:
import type { DependencyList, EffectCallback } from "react"
import { useEffect } from "react"
export function useEffectUnsafe(effect: EffectCallback, deps: DependencyList) {
let initialized = false
useEffect(() => {
if (!initialized) {
initialized = true
effect()
}
}, deps)
}
Please keep in mind that you should only resort to this solution if you absolutely have to!
I've had this issue where something like:
const [onChainNFTs, setOnChainNFTs] = useState([]);
would trigger this useEffect twice:
useEffect(() => {
console.log('do something as initial state of onChainNFTs changed'); // triggered 2 times
}, [onChainNFTs]);
I confirmed that the component MOUNTED ONLY ONCE and setOnChainNFTs was NOT called more than once - so this was not the issue.
I fixed it by converting the initial state of onChainNFTs to null and doing a null check.
e.g.
const [onChainNFTs, setOnChainNFTs] = useState(null);
useEffect(() => {
if (onChainNFTs !== null) {
console.log('do something as initial state of onChainNFTs changed'); // triggered 1 time!
}
}, [onChainNFTs]);
Here is the custom hook for your purpose. It might help in your case.
import {
useRef,
EffectCallback,
DependencyList,
useEffect
} from 'react';
/**
*
* #param effect
* #param dependencies
* #description Hook to prevent running the useEffect on the first render
*
*/
export default function useNoInitialEffect(
effect: EffectCallback,
dependancies?: DependencyList
) {
//Preserving the true by default as initial render cycle
const initialRender = useRef(true);
useEffect(() => {
let effectReturns: void | (() => void) = () => {};
/**
* Updating the ref to false on the first render, causing
* subsequent render to execute the effect
*
*/
if (initialRender.current) {
initialRender.current = false;
} else {
effectReturns = effect();
}
/**
* Preserving and allowing the Destructor returned by the effect
* to execute on component unmount and perform cleanup if
* required.
*
*/
if (effectReturns && typeof effectReturns === 'function') {
return effectReturns;
}
return undefined;
}, dependancies);
}
There is nothing to worry about. When you are running React in development mode. It will sometimes run twice. Test it in prod environment and your useEffect will only run once. Stop Worrying!!
It is strict mode in my case. Remove strict mode component at index.tsx or index.jsx
If someone comes here using NextJS 13, in order to remove the Strict mode you need to add the following on the next.config.js file:
const nextConfig = {
reactStrictMode: false
}
module.exports = nextConfig
When I created the project it used "Strict mode" by default that's why I must set it explicitly.
Ok this is maybe a bit late to comment on this - but I found a rather useful solution which is 100% react.
In my case I have a token which I'm using to make a POST request which logs out my current user.
I'm using a reducer with state like this:
export const INITIAL_STATE = {
token: null
}
export const logoutReducer = (state, action) => {
switch (action.type) {
case ACTION_SET_TOKEN :
state = {
...state,
[action.name] : action.value
};
return state;
default:
throw new Error(`Invalid action: ${action}`);
}
}
export const ACTION_SET_TOKEN = 0x1;
Then in my component I'm checking the state like this:
import {useEffect, useReducer} from 'react';
import {INITIAL_STATE, ACTION_SET_TOKEN, logoutReducer} from "../reducers/logoutReducer";
const Logout = () => {
const router = useRouter();
const [state, dispatch] = useReducer(logoutReducer, INITIAL_STATE);
useEffect(() => {
if (!state.token) {
let token = 'x' // .... get your token here, i'm using some event to get my token
dispatch(
{
type : ACTION_SET_TOKEN,
name : 'token',
value : token
}
);
} else {
// make your POST request here
}
}
The design is actually nice - you have the opportunity to discard your token from storage after the POST request, make sure the POST succeeds before anything. For async stuff you can use the form :
POST().then(async() => {}).catch(async() => {}).finally(async() => {})
all running inside useEffect - works 100% and within I think what the REACT developers had in mind - this pointed out that I actually had more cleanup to do (like removing my tokens from storage etc) before everything was working but now I can navigate to and from my logout page without anything weird happening.
My two cents...
I used CodeSandbox and removing prevented the issue.
CodeSandbox_sample

React Native Redux. State is being updated, yet the component is not being rerendered

I'm working on an app with redux.
In the following snippet I toggle the appEnabled state:
...
render() {
const { appEnabled } = this.props;
const { updatePreferences } = this.props;
return (
<View style={styles.preferences}>
<Text>App enabled: {appEnabled + ''}</Text>
<Button onPress={() => updatePreferences({ appEnabled: !appEnabled })}>Toggle AppEnabled</Button>
</View>)
}
Here are the mapping functions:
function mapStateToProps({ preferences }) {
return {
appEnabled: preferences.appEnabled,
};
}
function mapDispatchProps(dispatch) {
return {
getPreferences,
updatePreferences: (preferences) => dispatch(updatePreferencesAction(preferences))
};
}
export default connect(mapStateToProps, mapDispatchProps)(PreferenceScreen);
The updating of the state works fine. Yet the component's rerendering does not occur.
I know that common cause can be accidental state mutation. I made sure that reducer function returns new object each time:
export default function preferenceReducer(state = { ...defaultState }, action = {}) {
// console.log("reduces is called")
switch (action.type) {
case UPDATE_PREFERENCES :
return { ...state, ...action.preferences };
default:
return state;
}
}
// Action creators
export function updatePreferencesAction(preferences) {
return { type: UPDATE_PREFERENCES, preferences };
}
What could be the reason of the component not being rerendered in my case?
It's difficult to tell without seeing the full code, if you add a jsfiddle it's easier to help.
Anyway, your component seems fine and it should update when props change. The way you update your state in your reducer is the only thing that seems a bit odd. I can't tell the shape of your state just by looking a your reducer but, for instance, why are you doing state = {...defaultState} instead of state = defaultState?
return {...state, ...action.preferences } is also dubious. In most cases state is updated like this: return { ...state, preferences: action.preferences } since it makes the change your reducer is making much clearer.
I'd recommend you make these changes and then thoroughly debug with redux devtools.
Change
function mapStateToProps({ preferences }) {
return {
appEnabled: preferences.appEnabled,
};
}
To
function mapStateToProps({ appEnabled }) {
return {
appEnabled: appEnabled,
};

Updating redux state doesn't call componentWillReceiveProps

I'm working on an app at the moment and although I'm facing the same issue as here, Updating Redux state does not trigger componentWillReceiveProps.
I've read through this answer and am not mutating the state and I can see the different state when I log in mapStateToProps, but my componentWillReceiveProps is not being fired.
My code is as follows:
const mapReducer = (state = {}, action) => {
switch (action.type) {
case 'SET_MARKER':
return action.selectedMarker;
default:
return state
}
}
export default mapReducer
//mapActions.js
export const setMarker = (selectedMarker) => {
//Used to set the one that the user has selected.
return {
type: 'SET_MARKER',
selectedMarker
}
}
//InformationScreen.js
const mapStateToProps = (state) => {
console.log('returning in mapStateToProps');
console.log(state.mapReducer);
//Here I see that state.mapReducer is different everytime.
return {
marker: state.mapReducer,
user: state.userReducer,
completed: state.completedReducer
}
}
class InformationScreen extends React.Component {
componentWillReceiveProps(nextProps) {
//No logs in here.
console.log('Receiving props and the marker is');
console.log(nextProps.marker);
}
render() {
const { marker } = this.props;
console.log(marker);
// Here the marker does update.
return(<Text> Hello world </Text>);
}
}
export default connect(mapStateToProps)(InformationScreen);
import app from './reducers';
let store = createStore(app);
export default class App extends React.Component {
state = {
isLoadingComplete: false,
};
render() {
if (!this.state.isLoadingComplete && !this.props.skipLoadingScreen) {
return (
<AppLoading
startAsync={this._loadResourcesAsync}
onError={this._handleLoadingError}
onFinish={this._handleFinishLoading}
/>
);
} else {
return (
<ActionSheetProvider>
<Provider store={store}>
<View style={styles.container}>
{Platform.OS === 'ios' && <StatusBar barStyle="default" />}
{Platform.OS === 'android' &&
<View style={styles.statusBarUnderlay} />}
<RootNavigation />
</View>
</Provider>
</ActionSheetProvider>
);
}
}
}
//index.js
import { combineReducers } from 'redux'
import mapReducer from './mapReducer'
const app = combineReducers({
mapReducer,
//other reducers
})
export default app
//Dispatching the action from
import { connect } from 'react-redux';
import { setMarker } from '../actions/mapActions';
import { Container, Header, Tab, Tabs, TabHeading, List, ListItem, Left, Thumbnail, Body, Separator, Badge, Right} from 'native-base';
import _ from 'lodash';
import GLOBALS from '../constants/Globals';
const mapStateToProps = (state) => {
return {
user: state.userReducer,
challenges: state.allChallengesReducer
}
}
class MyChallengesScreen extends React.Component {
static navigationOptions = {
title: 'My Challenges',
headerTintColor: 'rgb(255, 255, 255)',
headerStyle: { backgroundColor: 'rgba(77, 90, 139, 1)'}
};
componentDidMount() {
this.handleRefresh();
}
componentWillReceiveProps(nextProps) {
if (nextProps.user.facebookId) {
this.handleRefresh(nextProps);
}
}
constructor (props) {
super(props);
this.state = {
refreshing: false,
}
}
markerPressed = (marker) => {
//setChallenge.
marker.coordinate = {latitude : +marker.latitude, longitude: +marker.longitude};
console.log('Setting the marker');
this.props.dispatch(setMarker(marker));
this.props.navigation.navigate('InformationScreen');
}
render() {
return (
<Button onPress={() => this.markerPressed()}></Button>
)
}
}
export default connect(mapStateToProps)(MarkersScreen);
I hope someone else has seen something similar to this before. Thanks in advice for any help with this.
Edit: So unfortunately I still haven't been able to solve this yet. But I have found something pretty interesting when using the Redux debugger. componentWillReceiveProps is called when after dispatching the action I then 'skip' that action. Seems pretty strange, but at least it's something. Time to continue digging.
connect will shallow compare the output of mapStateToProps to the previous output of mapStateToProps. If there are no changes, it will not re-render the connected component, i.e. InformationScreen. As you said that you are "definitely mutating the state" the shallow compare will find no difference between the outputs of mapStateToProps.
You can override this behaviour of avoiding re-render by passing in the correct options. connect accepts options as the 4th argument, which is an object for which you will need to set pure: false.
refer to https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options
[pure] (Boolean): If true, connect() will avoid re-renders and calls to mapStateToProps, mapDispatchToProps, and mergeProps if the relevant state/props objects remain equal based on their respective equality checks. Assumes that the wrapped component is a “pure” component and does not rely on any input or state other than its props and the selected Redux store’s state. Default value: true
Your reducer should only return a new object representing the state (not mutating the current state).
Your reducer should look like this
const mapReducer = (state = {selectedMarker: null}, action) => {
switch (action.type) {
case 'SET_MARKER':
return Object.assign({}, state, {
selectedMarker: action.selectedMarker
});
default:
return state
}
}
Object.assign mutates the first argument by adding all attributes that exist in all arguments after. Meaning it will mutate the {} by first adding the attributes in state then adding the attribute selectedMarker: action.selectedMarker. If state already has a selectedMarker then that will be overwritten in the new object.
and in your mapStateToProps
const mapStateToProps = (state) => {
return {
marker: state.mapReducer.selectedMarker,
...
}
}
Object Mutation
With the console logs showing different values after mapStateToProps problem is that you cannot visually tell if it's one mutated object or not. What is happening in connect is that new marker prop is tested for strict equality (===) against previous marker prop. Thing is that it doesn't matter how does the object look like, what properties it has etc. Only thing that is checked is if the object reference is the same
https://redux.js.org/basics/reducers#handling-actions
https://redux.js.org/basics/reducers#note-on-object.assign
The real problem is here:
markerPressed = (marker) => {
//setChallenge.
marker.coordinate = {latitude : +marker.latitude, longitude: +marker.longitude};
console.log('Setting the marker');
this.props.dispatch(setMarker(marker));
this.props.navigation.navigate('InformationScreen');
}
You are modifying the existing marker object, and dispatching an action to put that into the store. The reducer simply returns the marker object it was given.
We always emphasize that reducers need to be pure functions that update data immutably, but you actually need to create your new data immutably wherever you are creating it. If you have a reducer that just does return action.someValue, then the logic that created someValue needs to create it immutably.
So, for your case, your markerPressed() class method needs to make a copy of the marker and give it a new coordinate value.
Wow! I'm glad to say the issue has been fixed. It seems that the reason was that, since InformationScreen is in a StackNavigator, a new instance is created every time. Since the first time an object is created componentWillReceiveProps isn't called, this explains it. Additionally T
thanks to #markerikson, for pointing out that I should be recreating a marker object with each action dispatch. Thanks again, and so happy to be able to get back to work! It's probably a rookie error.
Reference: https://shripadk.github.io/react/tips/componentWillReceiveProps-not-triggered-after-mounting.html

Redux-form 6.0+ change all field values

I am trying to change multiple values in redux-form. I have them in one object so basically I want to override redux-form state values with my object values. One way to accomplish it is to run this.props.reset() followed by multiple this.props.change() events for each property. It works but it sends too many events and is slow. The second thing I tried is to run this.props.initialize(data,false) and this works but validation isn't rerun so I can easily submit the form without validation.
Is there a way to run one event to override form state with my object?
I am scared it is not possible. I had the same problem some time ago, and reading all the documentation in redux-form I got to conclude you have to use the action creators. Either change either autofill.
If you use initialize, you are initializing the values, it is meant to use for async initialization of data, therefore, it does not validate as you say.
Long ago in previous versions, they had a "defaultValue" concept. But they removed it. If you don't really need to have the last update, maybe it's worthy for you to check if that somehow would help you.
NOTE
I recommend you to follow this issue thread. They talk about it there.
It is possible. I achieved it in React using Redux via the Create-React-App file structure.
Using the stateProps/dispatchProps pattern.
You should already know about actions and reducers to use this.
Here is the project I originally started with https://medium.com/#notrab/getting-started-with-create-react-app-redux-react-router-redux-thunk-d6a19259f71f
I included that so you will know what I am talking about when I use terms like reducers and actions.
In you actions/index file
import makeAction from "./makeActionCreator";
const clearMultiplePropertiesInState = "clearMultiplePropertiesInState";
export default {
constants: {
clearMultiplePropertiesInState,
},
creators: {
clearMultiplePropertiesInState: makeAction(clearMultiplePropertiesInState
}
};
In your reducers/{your-reducer}.js
import actions from '../actions';
const { constants } = actions;
const INITIAL_STATE = {
name: "Dr Hibbert",
age: "40",
height: "180cm"
};
//#region const declarations
const { clearMultiplePropertiesInState } = constants;
//#endregion
export default function (state = INITIAL_STATE, action) {
switch (action.type) {
case clearMultiplePropertiesInState: {
var fields = action.data;
var theState = {
...state
};
for(var i = 0; i < fields.length; i++) {
theState[fields[i]] = "";
}
return theState;
}
default:
if (!action.type.includes('##')) {
console.log(`No action for: ${action.type} type`);
}
return state;
}
}
So the three items you want to clear are the state.name, state.age and state.height
import React from "react";
import { connect } from "react-redux";
import { Form, Icon, Button, Modal } from "semantic-ui-react";
import actions from "common/actions";
const { creators } = actions;
const MyComponent = ({ stateProps, dispatchProps }) => {
return (
<React.Fragment>
<Button
disabled={disableOkButton}
onClick={() => {
dispatchProps.clearMultiplePropertiesInState(["name", "age", "height"]);
}}
primary
labelPosition='right'
icon='checkmark'
content="Create Club"
loading={stateProps.modalOkButtonLoading}
/>
</React.Fragment>
);
}
function mapStatetoProps(state) {
return {
stateProps: {
name: state.name,
age: state.age,
height: state.height
}
};
}
function mapDispatchToProps(dispatch) {
return {
dispatchProps: {
clearMultiplePropertiesInState: (fieldNames) => {
dispatch(creators.clearMultiplePropertiesInState(fieldNames));
}
}
};
}
export default connect(mapStatetoProps, mapDispatchToProps)(MyComponent);
As I said you need to be well versed in using React with Redux to understand this but it is possible. This example shows I reset 3 values at the same time. So imaging passing new values as well...
I generally have a changeSinglePropInState action that I use (didnt include in code) which it passes the fieldName and the fieldValue it wants to change in state as I didnt want to create an action for every single item in my state.
Also if you can wrap your head around it, this changes one property of an object inside the state
case addItemToWishList: {
return {
...state,
buyer: {
...state.buyer,
wishlist: action.data
}
};
}

Categories