Calling one function in another and passing it id, react + redux - javascript

In React itself, I have the function getTodos(), in which it calls another functiongetTodo(). It passes res.data[0].id to the getTodo() function.
React
Demo here: https://stackblitz.com/edit/react-qvsjrz
Code below:
class App extends Component {
constructor() {
super();
this.state = {
todos: [],
todo: {}
};
}
componentDidMount() {
this.getTodos()
}
getTodos = () => {
axios({
url: 'https://jsonplaceholder.typicode.com/todos',
method: 'GET'
})
.then(res => {
this.setState({
todos: res.data,
}, () => this.getTodo(res.data[0].id))
})
.catch(error => {
console.log(error);
});
};
getTodo = (todoId) => {
axios({
url: `https://jsonplaceholder.typicode.com/todos/${todoId}`,
method: 'GET'
})
.then(res => {
this.setState({
todo: res.data
})
})
.catch(error => {
console.log(error);
})
}
render() {
console.log(this.state.todo);
return (
<div>
</div>
);
}
}
The above code tries to convert to react + redux.
React + redux
In actions, I declared two functions getTodo andgetTodos. Could someone advise me on how to call the getTodo function in thegetTodos function by passing the getTodo id function?
Demo here: https://stackblitz.com/edit/react-ewpquh?file=actions%2Findex.js
actions
import axios from 'axios';
export const GET_TODOS = 'GET_TODOS';
export const FETCH_SUCCESS = 'FETCH_SUCCESS';
export const FETCH_FAILURE = 'FETCH_FAILURE';
export const getTodos = () =>
dispatch => {
return axios({
url: 'https://jsonplaceholder.typicode.com/todos',
method: 'GET',
})
.then(({data})=> {
console.log(data);
dispatch({type: GET_TODOS, payload:{
data
}});
})
.catch(error => {
console.log(error);
dispatch({type: FETCH_FAILURE})
});
};
export const getTodo = () =>
dispatch => {
return axios({
url: 'https://jsonplaceholder.typicode.com/todos',
method: 'GET',
})
.then(({data})=> {
console.log(data);
dispatch({type: GET_TODOS, payload:{
data
}});
})
.catch(error => {
console.log(error);
dispatch({type: FETCH_FAILURE})
});
};
Todos
import React, { Component } from 'react';
import { connect } from 'react-redux';
import {getTodos} from '../.././actions';
class Todos extends Component {
componentDidMount() {
this.props.getTodos();
}
render() {
return (
<ul>
{this.props.todos.map(todo => {
return <li key={todo.id}>
{todo.title}
</li>
})}
</ul>
);
}
}
const mapStateToProps = state => {
console.log(state.todos);
const { todos } = state;
return {
todos
};
};
const mapDispatchToProps = dispatch => ({
getTodos: () => dispatch(getTodos())
});
export default connect(mapStateToProps, mapDispatchToProps)(Todos);
reducers
import {GET_TODOS} from '../../actions';
const initialState = {
todos: []
};
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case 'GET_TODOS':
return {
...state,
todos: action.payload.data
};
default:
return state;
}
};
export default rootReducer;
store
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from '../reducers';
const store = createStore(rootReducer, applyMiddleware(thunk));
export default store;

Instead of over-complicating your actions, you should have separate action types for different APIs.
GET_TODOS - For /todos API
GET_TO - For /todos/ API
To add getTodo method with ID, this is how I solved it -
For each li tag, add an onClick that calls your getTodo API. (This is done as an example for the sake of adding getTodo in the workflow.
return <li key={todo.id} onClick={() => this.handleClick(todo.id)}>
Add handleClick which calls getTodo method from props.
First add getTodo in your components mapDispatchToProps:
import { getTodo, getTodos} from '../.././actions';
const mapDispatchToProps = dispatch => ({
getTodos: () => dispatch(getTodos()),
getTodo: id => dispatch(getTodo(id))
});
Add handleClick -
handleClick = id => {
this.props.getTodo(id).then(() => {
console.log(`You Clicked: ${JSON.stringify(this.props.todo)}`)
})
}
Update your getTodo action to take ID as input:
NOTE: The added GET_TODO type
export const getTodo = (id) => dispatch => {
return axios({
url: `https://jsonplaceholder.typicode.com/todos/${id}`,
method: 'GET',
})
.then(({data})=> {
// console.log(data);
dispatch({type: GET_TODO, payload: data});
})
.catch(error => {
console.log(error);
dispatch({type: FETCH_FAILURE})
});
};
Separate out your reducers into todos and todo and use combineReducers from redux package -
const todos = (state = [], action) => {
const { type, payload } = action;
switch(type) {
case 'GET_TODOS':
return payload;
default:
return state;
}
}
const todo = (state = {}, action) => {
const { type, payload } = action;
switch(type) {
case 'GET_TODO':
return payload;
default:
return state;
}
}
const rootReducer = combineReducers({todos, todo});
Run the app and click on any item in the todo list. Console log for the clicked todo item is shown when API response for that ID is fetched.
The live sandbox is available here - https://stackblitz.com/edit/react-ndkasm

Related

SQLite not working with Redux in React Native

After introducing Redux to my React Native Expo app, whenever I try to interact with the database my app stops working.
actions.js:
export const SET_SELECTED_PLAYERS = "SET_SELECTED_PLAYERS"
export const SET_PLAYERS = "SET_PLAYERS"
export const SET_SELECTED_COURSE = "SET_SELECTED_COURSE"
export const SET_COURSES = "SET_COURSES"
//Player actions
export const setPlayers = (players) => (
{ type: SET_PLAYERS, payload: players, }
)
export const setSelectedPlayers = (players) => (
({ type: SET_SELECTED_PLAYERS, payload: players, })
)
export const setSelectedCourse = (course) =>
({ type: SET_SELECTED_COURSE, payload: course, })
export const setCourses = (courses) =>
({ type: SET_COURSES, payload: courses, })
reducers.js:
import { SET_PLAYERS, SET_SELECTED_PLAYERS, SET_SELECTED_COURSE, SET_COURSES } from "./actions"
const initialState = {
players: [],
selectedPlayers: [],
courses: [],
selectedCourse: null,
round: {}
}
export const playerReducer = (state = initialState, action) => {
switch (action.type) {
case SET_PLAYERS:
return { ...state, players: action.payload }
case SET_SELECTED_PLAYERS:
return { ...state, selectedPlayers: action.payload }
default:
return state
}
}
export const courseReducer = (state = initialState, action) => {
switch (action.type) {
case SET_SELECTED_COURSE:
return { ...state, selectedCourse: action.payload }
case SET_COURSES:
return { ...state, courses: action.payload }
default:
return state
}
}
store.js:
import { createStore, combineReducers, applyMiddleware } from "redux";
import { courseReducer, playerReducer } from "./reducers";
const rootReducer = combineReducers({ playerReducer, courseReducer })
export const Store = createStore(rootReducer)
SQLite used in component :
const dispatch = useDispatch()
const db = SQLite.openDatabase("players.db")
useEffect(() => {
db.transaction(tx => {
tx.executeSql("SELECT * FROM Player", [], (trans, result) => {
dispatch(setPlayers(result.rows._array))
})
})
}, [])
Table for Player exists and app worked before I introduced Redux. It interacts with Firebase and when fetching data from cloud Redux has no problems. What problems could it have with SQLite?
Use sqlite query in redux action
export const getUsers = () => {
try {
return async dispatch => {
const result = await fetch('https://jsonplaceholder.typicode.com/users', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
});
const json = await result.json();
if (json) {
dispatch({
type: GET_USERS,
payload: json
})
} else {
console.log('fetch user api error');
}
}
} catch (error) {
console.log('action error');
}
}

mapStateToProps not updating when redux store updates

I'm working in a react native app with react redux integration. When I call dispatch from a service my store is getting updated but somehow my component is not re-rendering.
Is it wrong to call dispatch from a service file and not from mapDispatchToProps function.
store.js
import { memesReducer } from './memesReducer'
export default combineReducers({
memesReducer
});
export default configureStore = () => {
const store = createStore(rootReducer);
return store;
}
memesReducer.js
const initialState = { memeList: [] }
export const memesReducer = (state = initialState, action) => {
switch (action.type) {
case LOAD_MEMES: {
return { ...state,
memeList: action.data
}
}
default:
return state;
}
}
memeService.js
import configureStore from '../redux/store';
import { loadMemes } from '../redux/actions';
const store = configureStore();
export const getMemesList = () => {
axios('https://jsonplaceholder.typicode.com/albums')
.then(response => {=
store.dispatch(loadMemes(response.data))
})
.catch(error => { console.error('getMemesList : ', error); })
}
memeActions.js
export const loadMemes = memesListData => ({
type: LOAD_MEMES,
data: memesListData
});
MemeList.js
class MemeList extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
getMemesList()
}
render() {
const memeListData = this.props.memeList.map((meme) => <MemeCard meme={meme} />)
return (
<Container>
<Content>
<List>
{ memeListData }
</List>
</Content>
</Container>
)
}
}
const mapStateToProps = state => {
return {
memeList: state.memesReducer.memeList,
}
}
export default connect(mapStateToProps)(MemeList);
memeActions.js
export const getMemesList = () => dispatch => {
axios("https://jsonplaceholder.typicode.com/albums")
.then(response => dispatch(loadMemes(response.data)))
.catch(error => {
console.error("getMemesList : ", error);
});
};
const loadMemes = memesListData => ({
type: "LOAD_MEMES",
data: memesListData
});
memeReducer.js
case "LOAD_MEMES": {
return { ...state, memeList: action.data };
}
index.js
export default combineReducers({
memesReducer: memeReducer
});
memeList.js
class memeList extends Component {
componentDidMount() {
this.props.getMemesList();
}
render() {
console.log(this.props.memeList);
return <div>MemeList</div>;
}
}
const mapStateToProps = state => ({
memeList: state.memesReducer.memeList
});
export default connect(
mapStateToProps,
{ getMemesList }
)(memeList);
Yeah bro it wouldn't work. You should call dispatch in a Redux connected component.
What you can do is await or attach a .then to the Service Call and THEN call the dispatch after the await or inside the .then.
call your actions like this then only i will work.
componentDidMount() {
this.props.getMemesList()
}
for your more clarification check this official documentation react redux

Calling the second function after calling the first function and fetching the data, react + redux

In pure React, I call the clickActive function in thegetTodos function after fetching the data from the server.
getTodos = () => {
const url = 'https://jsonplaceholder.typicode.com/todos';
const params = {
expand: 'createdBy, updatedBy'
};
axios({
method: 'GET',
url,
params
})
.then(res => {
this.setState({
todos: res.data
}, () => this.clickActive());
})
.catch(error => {
console.log(error);
});
};
clickActive = () => {
const activeTask = document.querySelector('.activeTask');
activeTask.click();
console.log('active')
};
How call function clickActive in React + Redux? I create the getTodos action in theactions folder. In the Todos component it calls this functiongetTodos by clicking the GET button. How to call the clickActive function after fetching the data? I put the clickActive function in thehelpers file. Should I import the clickActive function into the file actions/index.js?
Expected effect: click button GET -> call functiongetTodos -> call function clickActive
Demo here: https://stackblitz.com/edit/react-rk1evw?file=actions%2Findex.js
actions
import axios from 'axios';
export const GET_TODOS = 'GET_TODOS';
export const FETCH_SUCCESS = 'FETCH_SUCCESS';
export const FETCH_FAILURE = 'FETCH_FAILURE';
export const getTodos = () =>
dispatch => {
return axios({
url: 'https://jsonplaceholder.typicode.com/todos',
method: 'GET',
})
.then(({data})=> {
console.log(data);
dispatch({type: GET_TODOS, payload:{
data
}});
})
.catch(error => {
console.log(error);
dispatch({type: FETCH_FAILURE})
});
};
export const getTodo = () =>
dispatch => {
return axios({
url: 'https://jsonplaceholder.typicode.com/todos',
method: 'GET',
})
.then(({data})=> {
console.log(data);
dispatch({type: GET_TODOS, payload:{
data
}});
})
.catch(error => {
console.log(error);
dispatch({type: FETCH_FAILURE})
});
};
Todos
import React, { Component } from 'react';
import { connect } from 'react-redux';
import {getTodos} from '../.././actions';
import { clickActive } from '../../helpers';
class Todos extends Component {
constructor(props){
super(props);
}
render() {
return (
<>
<button onClick={this.props.getTodos}>GET</button>
<ul>
{this.props.todos.map(todo => {
return <li key={todo.id}>
{todo.title}
</li>
})}
</ul>
<div className="active">Active</div>
</>
);
}
}
const mapStateToProps = state => {
const { todos } = state;
return {
todos
};
};
const mapDispatchToProps = dispatch => ({
getTodos: () => dispatch(getTodos())
});
export default connect(mapStateToProps, mapDispatchToProps)(Todos);
helpers
export const clickActive = () => {
const activeTask = document.querySelector('.active');
activeTask.click();
console.log('click div');
};
Your clickActive function is a function that will interacts with created DOM, hence it should be called after render in componentDidUpdate and componentDidMount (or in useEffect hook if you would use hooks).
In componentDidMount/componentDidUpdate scenario I suggest add this those lifecycle methods in your Todos component:
componentDidMount() {
// call clickActive once after mount (but your todos are probably empty this time)
clickActive();
}
componentDidUpdate(prevProps) {
if (prevProps.todos !== this.props.todos) {
// call clickActive every time when todos is changed
// (i.e. it will be called when your asynchronous request change your redux state)
clickActive();
}
}

Sending actions in redux, if there are several cases in the reducer instruction in switch

In actions, I declared the actionexport const CLEAR_ARRAY_TODOS = 'CLEAR_ARRAY_TODOS';
I imported this action in reducers. And I created a new case case 'CLEAR_ARRAY_TODOS' in reducers` in the switch statement:
    
 case 'CLEAR_ARRAY_TODOS':
return {
todos: [],
};
In thetodos component I imported the action CLEAR_ARRAY_TODOS. And here I have a problem as in mapDispatchToProps in the functiongetTodos send this action CLEAR_ARRAY_TODOS and connect to the buttonClear Array Todos?
Demo here: https://stackblitz.com/edit/react-iuvdna?file=reducers%2Findex.js
Actions
import axios from 'axios';
export const GET_TODOS = 'GET_TODOS';
export const CLEAR_ARRAY_TODOS = 'CLEAR_ARRAY_TODOS';
export const FETCH_SUCCESS = 'FETCH_SUCCESS';
export const FETCH_FAILURE = 'FETCH_FAILURE';
export const getTodos = () =>
dispatch => {
return axios({
url: 'https://jsonplaceholder.typicode.com/todos',
method: 'GET',
})
.then(({data})=> {
console.log(data);
dispatch({type: GET_TODOS, payload:{
data
}});
})
.catch(error => {
console.log(error);
dispatch({type: FETCH_FAILURE})
});
};
export const getTodo = () =>
dispatch => {
return axios({
url: 'https://jsonplaceholder.typicode.com/todos',
method: 'GET',
})
.then(({data})=> {
console.log(data);
dispatch({type: GET_TODOS, payload:{
data
}});
})
.catch(error => {
console.log(error);
dispatch({type: FETCH_FAILURE})
});
};
Reducers
import {GET_TODOS, CLEAR_ARRAY_TODOS} from '../../actions';
const initialState = {
todos: []
};
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case 'GET_TODOS':
return {
...state,
todos: action.payload.data,
todo: action.payload.data[0]
};
case 'CLEAR_ARRAY_TODOS':
return {
todos: [],
};
default:
return state;
}
};
export default rootReducer;
Todos
import React, { Component } from 'react';
import { connect } from 'react-redux';
import {getTodos, CLEAR_ARRAY_TODOS} from '../.././actions';
class Todos extends Component {
componentDidMount() {
this.props.getTodos();
}
render() {
return (
<div>
<button>Clear array Todos</button>
<ul>
{this.props.todos.map(todo => {
return <li key={todo.id}>
{todo.title}
</li>
})}
</ul>
</div>
);
}
}
const mapStateToProps = state => {
console.log(state.todos);
console.log(state.todo);
const { todos } = state;
return {
todos
};
};
const mapDispatchToProps = dispatch => ({
getTodos: () => dispatch(getTodos())
});
export default connect(mapStateToProps, mapDispatchToProps)(Todos);
CLEAR_ARRAY_TODOS is not an action, is just a variable holding an action type string. You should add a clearTodos action
export const clearTodos = { type: CLEAR_ARRAY_TODOS }
or action creator
export const clearTodos = () => ({ type: CLEAR_ARRAY_TODOS })
and use that in your component mapDispatchToProps (like you do with getTodos)
import React, { Component } from 'react';
import { connect } from 'react-redux';
import {getTodos, clearTodos} from '../.././actions';
class Todos extends Component {
componentDidMount() {
this.props.getTodos();
}
render() {
return (
<div>
<button onClick={ this.props.clearTodos }>Clear array Todos</button>
<ul>
{this.props.todos.map(todo => {
return <li key={todo.id}>
{todo.title}
</li>
})}
</ul>
</div>
);
}
}
const mapStateToProps = state => {
console.log(state.todos);
console.log(state.todo);
const { todos } = state;
return {
todos
};
};
const mapDispatchToProps = dispatch => ({
getTodos: () => dispatch(getTodos()),
clearTodos: () => dispatch(clearTodos())
});
export default connect(mapStateToProps, mapDispatchToProps)(Todos);
Just add the clearTodos action into mapDispatchToProps
const mapDispatchToProps = dispatch => ({
getTodos: () => dispatch(getTodos()),
clearTodos: () => dispatch({type: CLEAR_ARRAY_TODOS})
});
Then you have to only handle this action when button is clicked, so add the onClick attribute over there:
<button onClick={this.props.clearTodos}>Clear array Todos</button>

Reducer in react/redux app only working for one action.type

In my store.js i have the following code:
import { createStore, applyMiddleware} from 'redux';
import thunk from 'redux-thunk'
const reducer = (state, action) => {
console.log(action.type)
if (action.type === 'LOAD_USERS') {
return {
...state,
users: action.users['users']
}
} else if (action.type === 'LOAD_CHATROOMS') {
return {
...state,
chatRooms: action.chatRooms['chatRooms']
}
}
return state;
}
export default createStore(reducer, {users:[], chatRooms:[]}, applyMiddleware(thunk));
the code inside the action.type === 'LOAD_CHATROOMS' is never accessed for some reason, this is the action file where i set the action type for the reducer:
import axios from 'axios'
axios.defaults.withCredentials = true
const loadUsers = () => {
return dispatch => {
return axios.get('http://localhost:3000/session/new.json')
.then(response => {
dispatch({
type: 'LOAD_USERS',
users: response.data
});
});
};
};
const logIn = user => {
return axios.post('http://localhost:3000/session', {
user_id: user.id
})
.then(response => {
//TODO do something more relevant
console.log('loged in');
});
};
const loadChatRooms = () => {
return dispatch => {
return axios.get('http://localhost:3000/session/new.json')
.then(response => {
dispatch({
type: 'LOAD_CHATROOMS',
chatRooms: response.data
});
});
};
};
const enterChatRoom = chatrom => {
};
export { loadUsers, logIn, enterChatRoom, loadChatRooms};
The 'Load methods' get the data that i use to populate both components (one for users list and the other one for chatrooms list ), both components are called at the same level in the app.js file.
Basically the output that i'm getting is the first component (users) as expected with the correct list, and the chatrooms component is also rendered but the data is not loaded (since it's corresponding reducer block is not accessed).
Thanks a lot for reading :)

Categories