I have some multiple states and functions to set the state using useState hook.
on each setstate component gets re-render. I want to re-render only functions of setstate fully completed.
for example:
in app.js
const [a, setA] = useState(() => false);
const [b, setB] = useState(() => {});
const [c, setC] = useState(() => props.c);
const [d, setD] = useState(null);
const [e, setE] = useState(null);
const [f, setF] = useState(null);
const [g, setG] = useState('');
const func1 = useCallback(data => {
setA(true);
setB(true);
setC(data && data.c);
setD();
setE(isChecked ? 'a' : 'b');
}, []);
const func2 = useCallback(data => {
setA(false);
setB(false);
}, []);
const func3 = useCallback(data => {
setC(false);
setD(false);
}, []);
const events = {
func1,
func2,
func3
};
const handleMessage = e => {
const { eventName, data } = e.data;
const handleEvents = eventName ? events[toCamel(eventName)] : null;
if (handleEvents && typeof handleEvents === 'function') {
handleEvents(data);
}
};
useLayoutEffect(() => {
iframe.addEventListener('message', handleMessage, true);
return () => {
iframe.removeEventListener('message', handleMessage, true);
};
}, []);
return (
<Fragment>
{a && (
Component1
)}
{b && c && (
Component2
)}
</Fragment>
);
On each func1 and func2 setA and setB return statement rerender the element. I don't want to re-render on each setA,setB,setC etc. once func1 or func2 fully completes only want rerender the components. As am new to hooks, can someone help with this?
if your state values are so deeply connected and you want to make sure everything is updated at once:
const [state, setState] = useState({
a: () => false,
b: () => {},
c: () => props.c,
d: null,
e: null,
f: null,
g: ''
});
const updateState = newState => setState(Object.assign({}, state, newState));
now when you want to update your state just use updateState like this:
const func1 = useCallback(
(data) =>
updateState({
a: true,
b: true,
c: data && data.c,
d: undefined,
e: isChecked ? 'a' : 'b',
}),
[],
);
you can store all your variable in one state like this:
const [state,setState] = useState({ a:false, b:{}, c:props.c,d:null})
const func1 = useCallback(data => {
setstate({a:true,b:true,c:data && data.c,d:null})
}, []);
Related
Actual code :
const [success, setSuccess] = React.useState(false);
const [ingredients, setIngredients] = React.useState([]);
const [ingredient, setIngredient] = React.useState("");
<input data-test="app-input" type="text" value={ingredient} onChange={(event) =>
setIngredient(event.target.value)} />
<button data-test="app-button"
onClick={() => {
if (ingredient) {
if (ingredients.length === 2) {
setSuccess(true);
}
setIngredients([...ingredients, ingredient]);
}
}}>Add<button>
Test code below :
Here only the on change input value is called mockSetSuccess
test("success state updated when click of the button", () => {
const mockSetSuccess = jest.fn();
React.useState = jest.fn(() => [false, mockSetSuccess]);
const wrapper = shallow(<App />);
const input = wrapper.find("[data-test='app-input']");
const button = wrapper.find("[data-test='app-button']");
input.simulate("change", { target: { value: "hello" } });
button.simulate("click", {});
expect(mockSetSuccess).toHaveBeenCalledWith(true);
});
Can any body help to check the success state and the ingredients array state value
I have these properties declared in my app:
const [lockfileData, setLockFileData] = useState({});
const [socket, setSocket] = useState<RiotWSProtocol>(null);
const [api, setApi] = useState<LoLAPI>(null);
const [champions, setChampions] = useState<Champion[]>([]);
const [summoner, setSummoner] = useState<Summoner>(null);
const [autoAcceptQueue, setAutoAccept] = useState(true);
const [instalockEnabled, setEnableInstalock] = useState(true);
const [selectedChampion, setSelectedChampion] = useState<Champion>(null);
const [callRoleEnabled, setCallRoleEnabled] = useState(true);
const [selectedRole, setSelectedRole] = useState<Role>('Mid');
I have an event handler in my useEffect hook, and inside that it handles more events:
const onJsonApiEvent = useCallback(
(message: any) => {
//console.log(message);
if (
message.uri === '/lol-matchmaking/v1/ready-check' &&
autoAcceptQueue
) {
if (
message.data?.state === 'InProgress' &&
message.data?.playerResponse !== 'Accepted'
) {
api.acceptQueue();
}
} else if (
message.uri === '/lol-champ-select/v1/session' &&
message.eventType === 'Update'
) {
console.log('enabled?', instalockEnabled)
if (instalockEnabled) {
const myCellId = message.data.localPlayerCellId as number;
const myAction = (message.data.actions[0] as any[]).find(
(x) => x.actorCellId === myCellId
);
if (
!myAction.completed &&
myAction.isInProgress &&
myAction.type === 'pick'
) {
api.pickAndLockChampion(1, myAction.id);
}
console.log('myAction', myAction);
}
}
},
[api, autoAcceptQueue, instalockEnabled]
);
const onSocketOpen = useCallback(() => {
console.log('socket', socket);
if (socket) {
socket.subscribe('OnJsonApiEvent', onJsonApiEvent);
}
}, [onJsonApiEvent, socket]);
const onConnect = useCallback((data: LCUCredentials) => {
setLockFileData(data);
const lolApi = new LoLAPI(data);
setApi(lolApi);
lolApi.getOwnedChampions().then((champs) => {
setSelectedChampion(champs[0]);
setChampions(champs);
});
lolApi.getCurrentSummoner().then((summoner) => {
setSummoner(summoner);
});
const wss = new RiotWSProtocol(
`wss://${data.username}:${data.password}#${data.host}:${data.port}`
);
setSocket(wss);
}, []);
useEffect(() => {
if (socket) {
socket.on('open', onSocketOpen);
}
connector.on('connect', onConnect);
connector.start();
return () => {
connector.stop();
};
}, [onConnect, onSocketOpen, socket]);
The dependencies appear to be correct, so it should be using the up to date values in each handler.
However, inside the onJsonApiEvent handler, properties such as instalockEnabled are always the default value.
I am updating the value of instalockEnabled in a component on my page:
<FormControlLabel
control={
<Checkbox
checked={instalockEnabled}
name="instalockEnabled"
color="primary"
onChange={handleInstalockEnableChange}
/>
}
label="Enabled"
/>
And its handler looks like this:
const handleInstalockEnableChange = (
e: React.ChangeEvent<HTMLInputElement>
) => {
setEnableInstalock(e.target.checked);
};
How come this is happening when it is a dependency?
The janky solution I've come up with for now is to have a separate variable that is useRef and update that at the same time as updating the state, therefore it persists:
const [instalockEnabled, setEnableInstalock] = useState(true);
const instalockEnabledRef = useRef(instalockEnabled);
const handleInstalockEnableChange = (
e: React.ChangeEvent<HTMLInputElement>
) => {
setEnableInstalock(e.target.checked);
instalockEnabledRef.current = e.target.checked;
};
And then just use instalockEnabledRef.current inside of the event handlers where it needs to know the current value.
Let's say I have this:
function Example(props) {
const { prop1, prop2 } = props;
const latestProp1 = useRef(prop1);
useEffect(() => {
latestProp1.current = prop1;
}, [prop1]);
useEffect(() => {
console.log(latestProp1.current);
}, [prop2]);
return null;
}
If both prop1 and prop2 change in the same re-render, is it guaranteed that the console would log the new prop1 value?
And if I swap the useEffect hooks' position -
useEffect(() => {
console.log(latestProp1.current);
}, [prop2]);
useEffect(() => {
latestProp1.current = prop1;
}, [prop1]);
would it be guaranteed that the console would log the old prop1 value (from before the render)?
Yes, if you follow the Rules of Hooks, the call order ensured (hooks are called via array index, check how hooks implemented).
By following this rule, you ensure that Hooks are called in the same order each time a component renders.
Therefore, by simulating a batched set state (both prop1 and prop2 change in the same re-render) you can notice that first will always be logged before second:
function Example(props) {
const { prop1, prop2 } = props;
React.useEffect(() => {
console.log("first");
}, [prop1]);
React.useEffect(() => {
console.log("second");
}, [prop2]);
return <>Example</>;
}
function Component() {
const [counter, setCounter] = React.useState(0);
const [counter2, setCounter2] = React.useState(0);
const together = () => {
setCounter2((p) => p + 1);
setCounter((p) => p + 1);
};
return (
<>
<Example prop1={counter} prop2={counter2} />
<button onClick={together}>together</button>
</>
);
}
I would like to rewrite the function component into a state component. I have a problem with useCallback. Please help.
component function:
const App = () => {
const [elements, setElements] = useState([]);
const [history, setHistory] = useState([]);
const [currentHistoryIndex, setCurrentHistoryIndex] = useState(0);
const handleOnHistoryPush = useCallback(() => {
setHistory([...history, elements]);
setElements([]);
}, [elements, history]);
const appendElement = useCallback(
value => setElements([...elements, value]),
[elements]
);
class component:
state = {
elements: [],
history: [],
currentHistoryIndex: 0
}
handleOnHistoryPush = useCallback(() => {
this.setState({history: ([...this.state.history, this.state.elements])});
this.setState({elements:([])});
}, [this.state.elements, this.state.history]);
appendElement = useCallback(
value => this.setState({elements:([...this.state.elements, value])}),
[this.state.elements]
);
this is a correct way to use state components callback function
state = {
elements: [],
history: [],
currentHistoryIndex: 0
}
handleOnHistoryPush = () => {
this.setState({history: ([...this.state.history, this.state.elements])});
this.setState({elements:([])});
};
appendElement = value => this.setState({elements:([...this.state.elements, value])});
I am currently working on a class-based function. Trying to convert the class to a stateless function, followed by refactoring my code for each event handler from this.SetState to useState (in this case setMovies).
This is my code (partial code):
const Movies = () => {
const [movies, setMovies] = useState(getMovies());
const [disabled, setDisabled] = useState(true);
const [pageSize, setPageSize] = useState(4);
const sortBy = sortType => {
setMovies(movies.filter(sortType));
setDisabled(false);
// this.setState({
// movies: this.state.movies.sort(sortType),
// isDisabled: false,
// });
};
it seems that It is not possible to filter this state. I am able to change to boolean but can't filter my Array. Is there a way to filter using Hooks?
Thanks in advance.
Nothing changes...
const List = () => {
const [items, setItems] = useState([1, 2, 3, 4, 5, 6])
const filterEvenResults = () => setItems(items => items.filter(x => x % 2))
return (
<div>
{
items.map(item => <p key={item}>{item}</p>)
}
<button onClick={filterEvenResults}>Filter</button>
</div>
)
}