So Im having an issue with a react/redux project. The program stops outputting information when the first onSetTechnicianXXX call is made. The interesting thing is I successfully perform multiple onSetGetData actions as well as an onSetTechnicianId (inside the parent file). I get no errors. Below is the debug code:
console.log("1");
const technician = fetchedTechnicians[0];
console.log("Tech.Intake.Name: " + technician.intakeData.name);
console.log("2");
const name = technician.intakeData.name;
console.log("Name: " + name);
this.onSetTechnicianName(name);
console.log("3");
this.onSetTechnicianName(technician.intakeData.name);
console.log("4");
this.onSetTechnicianCerts(technician.intakeData.certifications);
console.log("5");
this.onSetTechnicianPhone(technician.intakeData.phone);
console.log("6");
this.onSetTechnicianEmail(technician.intakeData.email);
console.log("7");
this.onSetTechnicianServicebay(technician.intakeData.servicebay);
console.log("8");
========Output from the Chrome DevTools Console========
1 TechnicianEditForm.js:118
Tech.Intake.Name: Joe Technician TechnicianEditForm.js:120
2 TechnicianEditForm.js:121
Name: Joe Technician TechnicianEditForm.js:123
========Output from the Chrome Redux DevTool=========
selectedTechnicianId(pin): "-LnECtv2Ms40OaFBu2t0"
getdata(pin): false
name(pin): ""
certifications(pin): ""
phone(pin): ""
email(pin): ""
servicebay(pin): ""
These are the React/Redux files for processing the state
===========actionTypes.js:==========================
export const SET_TECHNICIANID = 'SET_TECHNICIANID';
export const SET_GETDATA = 'SET_GETDATA';
export const SET_TECHNICIAN_NAME = 'SET_TECHNICIAN_NAME';
export const SET_TECHNICIAN_CERTS = 'SET_TECHNICIAN_CERTS';
export const SET_TECHNICIAN_PHONE = 'SET_TECHNICIAN_PHONE';
export const SET_TECHNICIAN_EMAIL = 'SET_TECHNICIAN_EMAIL';
export const SET_TECHNICIAN_SERVICEBAY = 'SET_TECHNICIAN_SERVICEBAY';
=================actions.js:===========================
import * as actionTypes from './actionTypes';
export const setTechnicianId = (selectedTechnicianId) => {
return {
type: actionTypes.SET_TECHNICIANID,
selectedTechnicianId: selectedTechnicianId
};
};
export const setGetData = (getdata) => {
return {
type: actionTypes.SET_GETDATA,
getdata: getdata
};
};
export const setTechnicianName = (name) => {
return {
type: actionTypes.SET_TECHNICIAN_NAME,
name: name
};
};
##### Others Not Shown But Simular #####
=================reducer.js:================
import * as actionTypes from './actionTypes';
import { updateObject } from './utility';
const initialState = {
selectedTechnicianId: '',
getdata: false,
name: '',
certifications: '',
phone: '',
email: '',
servicebay: ''
}
const baseReducer = (state = initialState, action) => {
switch (action.type) {
case actionTypes.SET_TECHNICIANID:
return updateObject(state, { selectedTechnicianId: action.selectedTechnicianId });
case actionTypes.SET_GETDATA:
return updateObject(state, { getdata: action.getdata });
case actionTypes.SET_TECHNICIAN_NAME:
return updateObject(state, { name: action.name });
##### Others Not Shown But Simular #####
=======================index.js:=====================
export {
setTechnicianId,
setGetData,
setTechnicianName,
setTechnicianCerts,
setTechnicianPhone,
setTechnicianEmail,
setTechnicianServicebay,
} from './actions';
===========Part of the TechnicianEditForm.js============
const mapStateToProps = state => {
return {
selectedTechnicianId: state.selectedTechnicianId,
getdata: state.getdata,
name: state.name,
certifications: state.certifications,
phone: state.phone,
email: state.email,
servicebay: state.servicebay
};
};
const mapDispatchToProps = dispatch => {
return {
onSetTechnicianId: (selectedTechnicianId) => dispatch(actionTypes.setTechnicianId(selectedTechnicianId)),
onSetGetData: (getdata) => dispatch(actionTypes.setGetData(getdata)),
onSetTechnicianName: (name) => dispatch(actionTypes.setTechnicianName(name)),
onSetTechnicianCerts: (certifications) => dispatch(actionTypes.setTechnicianCerts(certifications)),
onSetTechnicianPhone: (phone) => dispatch(actionTypes.setTechnicianPhone(phone)),
onSetTechnicianEmail: (email) => dispatch(actionTypes.setTechnicianEmail(email)),
onSetTechnicianServicebay: (servicebay) => dispatch(actionTypes.setTechnicianServicebay(servicebay)),
}
};
export default connect(mapStateToProps, mapDispatchToProps)(withRouter(TechnicianEditForm), axios);
Related
I am currently trying to get the array of objects namely -Animearray in another component and i am getting an error of undefined in the console.
Here is the code of the Store component
import {
configureStore,
createAsyncThunk,
createSlice,
} from "#reduxjs/toolkit";
import { API_KEY, TMBD_BASE_URL } from "../utils/constent";
import axios from "axios";
const initialState = {
movies: [],
genresLoaded: false,
genres: [],
};
const initialAnime = {
anime: [],
genresLoaded: false,
genres: [],
};
const createArrayfromRawdata = (array, moviesArray, genres) => {
array.forEach((movie) => {
const movieGenres = [];
movie.genre_ids.forEach((genre) => {
const name = genres.find(({ id }) => id === genre);
if (name) movieGenres.push(name.name);
});
if (movie.backdrop_path)
moviesArray.push({
id: movie.id,
name: movie?.original_name ? movie.original_name : movie.original_title,
image: movie.backdrop_path,
genres: movieGenres.slice(0, 3),
});
});
};
async function createAnimeFromRawData(rawData, animeArray) {
const data = rawData;
console.log(animeArray);
for (let i = 0; i < data.length; i++) {
const anime = data[i];
if (anime) {
const genreArr = anime.genres.map((genre) => genre.name);
animeArray.push({
name: anime.title,
genre: genreArr,
score: anime.score,
image: anime.images.jpg.image_url,
trailer: anime.trailer.embed_url,
episodes: anime.episodes,
synopsis: anime.synopsis,
});
}
}
console.log(animeArray);
return animeArray;
}
export const RawdataAnime = async () => {
const Animearray = [];
for (let i = 1; Animearray.length < 60 && i < 10; i++) {
const { data } = await axios.get(`https://api.jikan.moe/v4/top/anime`); // Equivalent to response.data
const results = data?.data || [];
try {
await createAnimeFromRawData(results, Animearray);
await new Promise((resolve) => setTimeout(resolve, 1000));
} catch (error) {
console.error(error);
}
}
return Animearray;
};
const rawData = async (api, genres, paging) => {
const moviesArray = [];
for (let i = 1; moviesArray.length < 60 && i < 10; i++) {
const {
data: { results },
} = await axios.get(`${api}${paging ? `&page=${i}` : ""}`);
createArrayfromRawdata(results, moviesArray, genres);
}
return moviesArray;
};
export const fetchMovies = createAsyncThunk(
"neflix/trending",
async ({ type }, thunkAPI) => {
const {
netflix: { genres },
} = thunkAPI.getState();
return rawData(
`${TMBD_BASE_URL}/trending/${type}/week?api_key=${API_KEY}`,
genres,
true
);
}
);
//`${TMBD_BASE_URL}/discover/${type}?api_key=${API_KEY}&with_genres=${genres}`
export const getGenres = createAsyncThunk("netflix/genres", async () => {
const {
data: { genres },
} = await axios.get(`${TMBD_BASE_URL}/genre/movie/list?api_key=${API_KEY}`);
return genres;
});
const netflixSlice = createSlice({
name: "netflix",
initialState,
extraReducers: (builder) => {
builder.addCase(getGenres.fulfilled, (state, action) => {
state.genres = action.payload;
state.genresLoaded = true;
});
builder.addCase(fetchMovies.fulfilled, (state, action) => {
state.movies = action.payload;
});
},
});
const animeSlice = createSlice({
name: "anime",
initialState: initialAnime,
extraReducers: (builder) => {
builder.addCase(RawdataAnime.fulfilled, (state, action) => {
state.anime = action.payload;
});
},
});
export const store = configureStore({
reducer: {
netflix: netflixSlice.reducer,
anime: animeSlice.reducer,
},
});
and turns out when I tried to use the animeArray in my main component it did not load
import BackgroundVid from "../components/BackgroundVid";
import { fetchMovies, getGenres, RawdataAnime, setAnime } from "../store";
import Slider from "../components/Slider";
export default function Netflix() {
const [scrolled, isScrolled] = useState(false);
const genresLoaded = useSelector((state) => state.netflix.genresLoaded);
const movies = useSelector((state) => state.netflix.movies);
const anime = useSelector((state) => state.anime.anime);
const dispatch = useDispatch();
useEffect(() => {
dispatch(getGenres());
onAuthStateChanged(firbaseauth, (user) => {
if (user) {
setUser(user);
} else {
setUser(null);
}
});
}, []);
useEffect(() => {
if (genresLoaded) {
dispatch(fetchMovies({ type: "all" }));
}
});
If you have any suggestions on how can i get the data from that component do let me know.
The targeted data is stored in the component as animeArray
From what I can tell, the RawdataAnime function isn't an action creator. If you want to have a RawdataAnime.fulfilled reducer case then it should be converted to an asynchronous action so when the returned Promise resolves, i.e. it is fulfilled, the reducer can handle the returned payload.
Example:
store
import {
configureStore,
createAsyncThunk,
createSlice,
} from "#reduxjs/toolkit";
import axios from "axios";
const initialAnime = {
anime: [],
genresLoaded: false,
genres: [],
};
...
function createAnimeFromRawData(rawData) {
return rawData.map((anime) => ({
id: anime.mal_id,
name: anime.title,
genre: anime.genres.map((genre) => genre.name),
score: anime.score,
image: anime.images.jpg.image_url,
trailer: anime.trailer.embed_url,
episodes: anime.episodes,
synopsis: anime.synopsis
}));
}
export const RawdataAnime = createAsyncThunk("anime/fetchAnime", async () => {
const { data } = await axios.get(`https://api.jikan.moe/v4/top/anime`); // Equivalent to response.data
const results = data?.data || [];
return createAnimeFromRawData(results);
});
...
const animeSlice = createSlice({
name: "anime",
initialState: initialAnime,
extraReducers: (builder) => {
builder.addCase(RawdataAnime.fulfilled, (state, action) => {
state.anime = action.payload;
});
}
});
export const store = configureStore({
reducer: {
netflix: netflixSlice.reducer,
anime: animeSlice.reducer
}
});
App
const dispatch = useDispatch();
const anime = useSelector((state) => state.anime.anime);
useEffect(() => {
dispatch(RawdataAnime());
}, []);
in react-redux:
API file, returnes data from DB:
import axios from "axios";
const URL = "http://127.0.0.1:8000/GetAllExercises/";
export function GetAllExercies(){
return new Promise((resolve) =>
axios.get(URL).then((res)=> resolve({data: res.data})))
}
Slice File, calling the async function and setting the payload to the varibale- exercises:
import { createAsyncThunk, createSlice, PayloadAction } from "#reduxjs/toolkit";
import { RootState, AppThunk } from "../../app/store";
import { GetAllExercies } from "../API/GetAllExercisesAPI";
export interface CounterState {
exercieses: string[];
status: "idle" | "loading" | "failed";
}
const initialState: CounterState = {
exercieses: [],
status: "idle",
};
export const GetAllExerciesAsync = createAsyncThunk(
"get_all_exercises/GetAllExercies",
async () => {
const response = await GetAllExercies();
return response;
}
);
export const GetAllExerciesSlice = createSlice({
name: "get_all_exercises",
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(GetAllExerciesAsync.pending, (state) => {
state.status = "loading";
})
.addCase(
GetAllExerciesAsync.fulfilled,
(state, action: PayloadAction<any>) => {
state.status = "idle";
console.log(action.payload);
state.exercieses = action.payload;
--> console.log(state.exercieses);
}
);
},
});
export const selectExercises = (state: RootState) => state.get_all_exercises.exercieses;
export default GetAllExerciesSlice.reducer;
at the line with arrow i get this at the console debugger(f12):
{data:Array(17)}
how can i accesses the data inside of it?
If you want access the data duting setting state then you can simply access data from payload
export const GetAllExerciesSlice = createSlice({
name: "get_all_exercises",
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(GetAllExerciesAsync.pending, (state) => {
state.status = "loading";
})
.addCase(
GetAllExerciesAsync.fulfilled,
(state, action: PayloadAction<any>) => {
state.status = "idle";
// data is inside action.payload
state.exercieses = action.payload.data;
console.log(state.exercieses);
}
);
},
});
Apparently you are assigning an object to your "exercieses" variable. If using redux there should be a reducer where you do this.
Something similar to this:
const { response } = action.payload
return {...state, exercises:response.data}
I don't understand why this error appears. I am trying to create profile feature that will get data with axios and will show it by userid. I tried a lot of things and also i did the same thing with other component and everything worked
ProfileContainer.js
import React from 'react';
import {connect} from 'react-redux';
import Profile from './Profile';
import setUserProfile from '../../redux/profile-reducer'
import * as axios from 'axios';
class ProfileContainer extends React.Component {
componentDidMount() {
axios.get(`https://social-network.samuraijs.com/api/1.0/profile/2`).then(response => {
this.props.setUserProfile(response.data);
});
};
render() {
return (
<Profile {...this.props} profile={this.props.profile} />
)
}
}
let mapStateToProps = (state) => ({
profile: state.profilePage.profile
});
export default connect(mapStateToProps,{setUserProfile})(ProfileContainer);
profile-reducer.js
const ADD_POST = 'ADD-POST'
const UPDATE_NEW_POST_TEXT = 'UPDATE-NEW-POST-TEXT'
const SET_USER_PROFILE = 'SET_USER_PROFILE';
let initialState = {
posts: [
{ id: 1, message: 'How are you bro?)', likesCount: 21312 },
{ id: 2, message: 'Have you ever been to Georgia?', likesCount: 31312312 },
],
newPostText: 'q',
profile: null
};
const profileReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_POST:
let newPost = {
id: 124331,
message: state.newPostText,
likesCount: 0
}
return { ...state, posts: [...state.posts, newPost], newPostText: '' }
case UPDATE_NEW_POST_TEXT:
return { ...state, newPostText: action.newText };
case SET_USER_PROFILE:
return { ...state, profile: action.profile };
default:
return state;
}
}
export const addPostActionCreator = () => ({ type: ADD_POST });
export const updateNewPostTextActionCreator = (text) => ({ type: UPDATE_NEW_POST_TEXT, newText: text });
export const setUserProfile = (profile) => ({ type: SET_USER_PROFILE, profile });
export default profileReducer;
You must use async/await in your code.
take a look at this article: https://medium.com/#matt.readout/using-javascripts-async-await-syntax-to-fetch-data-in-a-react-app-878b930cdc6f
you will find the same code there.
I was trying to implement a page-by-page onboarding signup screen for which the first page collects users horoscopic sign and in the next page, it asks for name. The thing is the sign_id gets replaced by name. Please check the codes below
action.js
import * as types from './types';
export function addNewUserRequest(values) {
console.log('action data', values);
return {
type: types.NEW_USER_REQ,
values,
};
}
reducer.js
import createReducer from '../lib/createReducer';
import * as types from '../actions/types';
const initialState = {
values: [],
};
export const newUserReducer = createReducer(initialState, {
[types.NEW_USER_REQ](state, action) {
console.table('reducer action test', state, action.values);
return {
...state,
values: action.values,
};
},
createreducer.js
export default function createReducer(initialState, handlers) {
return function reducer(state = initialState, action) {
if (handlers.hasOwnProperty(action.type)) {
return handlers[action.type](state, action);
} else {
return state;
}
};
}
Page1.js
const dispatch = useDispatch();
const onPress = (val) => {
console.log('SELECTED SIGN', val);
let value = {
sign_id: val,
};
NavigationService.navigate('Login3');
dispatch(newUserActions.addNewUserRequest(value));
};
Page2.js
const dispatch = useDispatch();
const handlePress = () => {
let value = {
name: userName,
};
dispatch(newUserActions.addNewUserRequest(value));
NavigationService.navigate('Login4');
};
Console
Change param in addNewUserRequest from values to value as only single value is passed. Then append action.value to state.values.
export function addNewUserRequest(value) {
console.log('action data', value);
return {
type: types.NEW_USER_REQ,
value,
};
}
export const newUserReducer = createReducer(initialState, {
[types.NEW_USER_REQ](state, action) {
console.table('reducer action test', state, action.value);
return {
...state,
values: { ...state.values, ...action.value }
};
},
Im relatively new to React and Redux, and I created a simple ajax Email form for learning. The issue i'm having is that after form submission I set the store state back to Initialstate, which should clear all fields but it doesn't. I can see the store changes in redux logger, *see image attached but these changes are not showing on the ui. Is my store not mapping to state correctly? Or am I mutating state somewhere?
My reducer looks like the following:
export default function contactForm(state = initialState.formValues, action) {
switch (action.type) {
case types.FORM_RESET:
return initialState.formValues;
case types.FORM_SUBMIT_SUCCESS:
return Object.assign({}, action.message);
default:
return state;
}
}
Combine Reducers:
import { combineReducers } from 'redux';
import message from './formReducer';
import ajaxCallsInProgress from './ajaxStatusReducer';
const rootReducer = combineReducers({
message,
ajaxCallsInProgress
});
My initialstate looks like:
export default {
formValues: {
name: '', email: '', message: '',
},
ajaxCallsInProgress: 0,
};
My Actions Look like this:
export function messageSuccess(message) {
return { type: types.FORM_SUBMIT_SUCCESS, message };
}
export function resetForm() {
return { type: types.FORM_RESET };
}
export function saveMessage(message) {
return function (dispatch) {
dispatch(beginAjaxCall());
return messageApi.saveMessage(message)
.then(() => {
dispatch(messageSuccess(message));
dispatch(resetForm());
}).catch((error) => {
dispatch(ajaxCallError(error));
throw (error);
});
}
}
In the view I am mapping state to props via:
constructor(props, context) {
super(props, context);
this.state = {
message: Object.assign({}, this.props.message),
}
}
render() {
return (
<ContactForm
onChange={this.updateMessageState}
onSubmit={this.submitForm}
message={this.state.message}
/>
);
}
function mapStateToProps(state) {
return {
message: state.message,
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(formActions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(ContactSection);
Log showing store changes
I would be very grateful to any advice.
I've updated my answer with the code that I think should work for your example. You were pretty close, however based on your comments on trying to combine two reducers, I've created two reducers so you can see how it works.
/* constants.js */
export default {
FORM_RESET: 'FORM_RESET',
FORM_SUBMIT: 'FORM_SUBMIT',
AJAX_REQUEST: 'AJAX_REQUEST'
};
/* form-values-reducer.js */
const initialState = {
name: '',
email: '',
message: ''
};
export default const formValuesReducer = (state = initialState, action) => {
switch (action.type) {
case Constants.FORM_SUBMIT:
return {
...state,
message: action.message
};
case Constants.FORM_RESET:
return {
..state,
name: '',
email: '',
message: ''
};
default:
return state;
}
};
/* ajax-request-reducer.js */
const initialState = {
ajaxRequestCount: 0
};
export default const ajaxRequestReducer = (state = initialState, action) => {
switch (action.type) {
case Constants.AJAX_REQUEST:
return {
...state,
ajaxRequestCount: state.ajaxRequestCount + 1
};
default:
return state;
}
};
/* action-creators.js */
export const resettedForm = () => {
return {
type: Constants.FORM_RESET
}
};
export const submittedForm = (message) => {
return {
type: Constants.FORM_SUBMIT,
message
}
};
export const ajaxRequested = () => {
return {
type: Constants.AJAX_REQUEST
}
};
/* actions */
export const resetForm = (dispatch) => {
return () => {
dispatch(resettedForm());
}
};
export const submitForm = (dispatch) => {
return (message) => {
dispatch(ajaxRequested());
dispatch(submittedForm(message));
}
};
/* reducers.js */
import { combineReducers } from 'redux';
import ajaxRequest from './ajax-request-reducer';
import formValues from './form-values-reducer';
export default combineReducers({
ajaxRequest,
formValues
});
/* Component */
import React from 'react';
import { connect } from 'react-redux';
import { resetForm, submitForm } from './actions';
const App = (props) => (
<div>Your app UI stuff goes here</div>
);
const mapStateToProps = (state) => {
return {
name: state.formValues.name,
email: state.formValues.email,
message: state.formValues.message,
ajaxRequestCount: state.ajaxRequest.ajaxRequestCount
};
};
const mapDispatchToProps = (dispatch) => {
return {
resetForm: resetForm(dispatch),
submitForm: submitForm(dispatch)
}
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
I've not run this through anything, so there may be some mistakes in the code here and there.
I added the following which updated the state. I'm not sure if this is best practise with Redux, but it worked
componentWillReceiveProps(nextProps) {
this.setState({ message: nextProps.mail });
}