I am learning react-redux, so now I am trying to create react-redux crud app, here is ny solution
Here is repo : repo demo
The button
<span className="delete_info" onClick={() => deleteComment(comment.id) }>Delete</span>
The action creator to delete element
export const removeComment = id =>{
return{
type: ActionTypes.DELETE_COMMENTS,
payload:id
}
}
// delete comments
export const deleteComment = id =>{
console.log('ids', id);
return dispatch =>{
dispatch(fetchCommentsRequest())
axios.delete(`/api/v1/todo/${id}`)
.then(response =>{
console.log('yeees mom', response.data)
dispatch(removeComment(id))
})
.catch(error =>{
const erroMsg =error.message;
console.log('eeeror', erroMsg)
dispatch(fetchCommentsFailure(erroMsg))
})
}
}
Here is my reducer
import * as ActionTypes from '../action-types'
const initialState ={
data:[],
error:'',
comments:[],
loading:false,
editing:false
}
const reducer = (state= initialState, action) => {
switch (action.type) {
case ActionTypes.FETCH_COMMENTS_REQUEST:
return{
...state,
loading: true,
}
case ActionTypes.FETCH_COMMENTS_SUCCESS:
return{
...state,
loading:false,
comments:action.payload,
error:''
}
case ActionTypes.FETCH_COMMENTS_FAILURE:
return{
...state,
loading:false,
error:action.payload
}
case ActionTypes.ADD_COMMENTS:
return{
...state,
comments:state.comments.concat(action.payload)
}
case ActionTypes.DELETE_COMMENTS:
return{
...state,
comments: state.comments.filter(comment =>comment.id !==action.payload)
}
case ActionTypes.EDIT_COMMENTS:
return{
...state,
comments: state.comments.map(comment =>comment.id === action.payload?{
...comment,
editing:!editing
}:comment)
}
default: // need this for default case
return state
}
}
export default reducer
Now when I click delete, I see on the console the ID from action creators, but the element is not removed
and no errors, what is wrong here?
There's a few knots; I made this simplified sandbox (mocking an api call) of how it should work:
https://codesandbox.io/s/wonderful-minsky-99xi4?file=/src/App.js:0-799
index
import React from "react";
import ReactDOM from "react-dom";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import thunkMiddleware from "redux-thunk";
import rootReducer from "./rootReducer";
import App from "./App";
const store = createStore(rootReducer, applyMiddleware(thunkMiddleware));
const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
rootElement
);
App
import React from "react";
import "./styles.css";
import { connect } from "react-redux";
import deleteRequest from "./deleteRequest";
const mapStateToProps = state => {
return {
comments: state.comments
};
};
const mapDispatchToProps = {
deleteRequest: deleteRequest
};
let App = ({ comments, deleteRequest }) => {
const makeDeleteRequest = id => {
deleteRequest(id);
};
return (
<div className="App">
{comments.map(comment => {
return (
<div key={comment.id}>
<p>{comment.text}</p>
<button onClick={() => makeDeleteRequest(comment.id)}>
Delete
</button>
</div>
);
})}
</div>
);
};
App = connect(
mapStateToProps,
mapDispatchToProps
)(App);
export default App;
reducer
const initialState = {
data: [],
error: "",
comments: [{ id: 1, text: "test1" }, { id: 2, text: "test2" }],
loading: false,
editing: false
};
function rootReducer(state = initialState, action) {
switch (action.type) {
case "DELETE_COMMENT":
return {
...state,
comments: state.comments.filter(comment => comment.id !== action.id)
};
default:
return state;
}
}
export default rootReducer;
async action
import deleteComment from "./deleteComment";
const mockAPI = new Promise(function(resolve, reject) {
setTimeout(() => resolve("deleted"), 2000);
});
const deleteRequest = id => {
return dispatch => {
const makeDeleteRequest = async () => {
await mockAPI;
dispatch(deleteComment(id));
};
makeDeleteRequest();
};
};
export default deleteRequest;
delete comment action
export default function deleteComment(id) {
return { type: "DELETE_COMMENT", id };
}
It looks like you're not actually dispatching the action, you're just returning an async action creator (deleteComment function). In order your code to work, you need to first add redux-thunk middleware to your redux store (so that you can use async action creators) and then, in your component, when you're calling deleteComponent, you have to wrap the call using redux dispatch.
If you're using a function component, you can add useDispatch hook and have something like:
import {useDispatch} from "react-redux";
// ...
function MyComponent() {
const dispatch = useDispatch();
// ...
return <span className="delete_info" onClick={() => dispatch(deleteComment(comment.id))}>Delete</span>
}
or you can just use the connect function to create a HOC and pass the dispatch function from the provider's context:
const ConnectedComponent = connect(undefined, dispatch => ({dispatch}))(MyComponent);
function MyComponent({dispatch}) {
return <span className="delete_info" onClick={() => dispatch(deleteComment(comment.id))}>Delete</span>;
}
Related
I dispatched an action in a react-redux application, the states of the reducer with that action was updated with the payload from the action as required. In addition to this, the states of the another reducer that does not not have such an action was updated with that same payload even when the corresponding action was not dispatched. what exactly is happening?
//my action for reducer 1
import { CREATE, UPDATE, DELETE, FETCH_ALL, ITEMS_LOADING } from '../constants/actionTypes';
import * as api from '../api/index';
export const getPosts = () => async (dispatch) => {
dispatch(setItemsLoading());
const data = await api.fetchPosts
.then((res) => {
dispatch({ type: FETCH_ALL, payload: res.data });
}).catch(() => {
console.log("there is error");
})
}
//my action for reducer 2
import { GET_POST } from '../constants/actionTypes';
import * as api from '../api/index';
export const getSinglePosts = (id) => (dispatch) => {
return{
type: GET_POST, payload: id
}
}
//reducer 1
import { CREATE, UPDATE, DELETE, FETCH_ALL, ITEMS_LOADING } from '../constants/actionTypes';
const initialState = {
posts: [],
allLoaded:false,
loading: false
}
export default (state = initialState, action) => {
switch (action.type) {
case DELETE:
return {
...state,
posts: state.posts.filter((post) => post._id !== action.payload)
};
case UPDATE:
return {
...state,
posts: state.posts.map((post) => post._id === action.payload._id ? action.payload : post)
};
case FETCH_ALL:
return {
...state,
posts: action.payload,
allLoaded:true,
loading: false
};
case CREATE:
return {
...state,
posts: [action.payload, ...state.posts]
};
case ITEMS_LOADING:
return {
...state,
loading: true
};
default:
return state;
}
}
//reducer2
import {
GET_POST
} from '../constants/actionTypes';
const initialState = {
info:null,
postLoaded:false
}
export default function (state = initialState, action) {
switch (action.type) {
case GET_POST:
return {
...state,
postLoaded: true,
info:action.payload
};
default:
return state;
}
}
//homepage where action was dispatched
import React, { useState, useEffect } from 'react';
import PageNavbar from './PageNavbar';
import { getPosts } from '../../myActions/posts';
import { useSelector, useDispatch } from 'react-redux';
//import { getSinglePosts } from '../../myActions/single';
import { useHistory } from 'react-router-dom';
import Button from '#material-ui/core/Button';
function MyBlog() {
const dispatch = useDispatch();
useEffect(() => {
dispatch(getPosts());
}, [dispatch]);
const posts = useSelector((state) => state.posts.posts);
const history = useHistory();
return (
<>
<PageNavbar />
<div align="center">
{posts.map((post) => (
<>
<Button href="/user/singlePost" color="primary">
{post.title}
</Button>
<br />
</>))}
</div>
</>
)
}
export default MyBlog
//root reducer
import { combineReducers } from 'redux';
import posts from './myposts';
import error from './error';
import auth from './auth';
import info from './singlePost';
export default combineReducers({
info,
posts,
auth,
error
})
//thanks
I'm trying pass the data from reducer to component and receive as props.
But the data return UNDEFÄ°NED, so I have tried console the data on reducer and action, but it's okey. There isn't any problem with the data coming from the API, but it always return to component undefined. Where is my fault?
Action
export default ProfileTab;
import axios from 'axios';
import { BASE, API_KEY } from '../config/env';
export const FETCHED_MOVIES = 'FETCHED_MOVIES';
export function fetchMovies() {
return (dispatch) => {
axios
.get(`${BASE}s=pokemon&apikey=${API_KEY}`)
.then((result) => result.data)
.then((data) =>
dispatch({
type: FETCHED_MOVIES,
payload: data.Search,
}),
);
};
}
Reducer
import { FETCHED_MOVIES } from '../actions/movies';
const initialState = {
fetching: false,
fetched: false,
movies: [],
error: {},
};
export default (state = initialState, action) => {
switch (action.type) {
case 'FETCHED_MOVIES':
return {
...state,
movies: action.payload,
};
default:
return state;
}
};
Component
import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { fetchMovies } from '../../actions/movies';
class Case extends Component {
static propTypes = {
movies: PropTypes.object.isRequired,
};
constructor(props) {
super(props);
}
componentDidMount() {
this.props.fetchMovies();
}
onChangeHandler = (e) => {
this.setState({
input: e.target.value,
});
};
render() {
console.log(this.props.movies);
return (
<div>
<div className="movies-root">
<div className="movies-wrapper">
<div className="movies-container safe-area">
<h1>mert</h1>
</div>
</div>
</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
movies: state.movies,
};
};
const mapDispatchToProps = {
fetchMovies,
};
export default connect(mapStateToProps, mapDispatchToProps)(Case);
Do this in the connect statement:
export default connect(mapStateToProps,{fetchMovies})(Case);
And remove the mapDispatchToProps function from your code.
Dispatching props as an object is quite incorrect. Try this, and it should work.
That's because your mapDispatchToProps function should return an object and take dispatch as parameter. Each field in your returned object should contain a function that dispatches your action.
So try something like this:
const mapDispatchToProps = dispatch => {
return {
fetchMovies: () => dispatch(fetchMovies())
}
}
Although there's already an accepted answer, I'm not sure how correct it is, as it's completely valid to pass mapDispatchToProps the way you did with the latest react (16.13.1) and react-redux (7.2.1) versions (I'm not sure about earlier versions).
Now, assuming your question contains the whole code, there are two important things missing:
Creating the store:
import { createStore } from "redux";
const store = createStore(reducer);
and passing it to the Provider component:
<Provider store={store}>
If you go ahead and do as above, you'll see that this.props.fetchMovies emits the following error:
Actions must be plain objects. Use custom middleware for async actions.
To fix it, do as it says and add a middleware, e.g. thunk:
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
const store = createStore(rootReducer, applyMiddleware(thunk));
What follows is the full code. Note that I "split" fetchMovies into two functions: sync and async, for illustrating the difference usage between the two. I also modified your code (made is shorter, mostly) for this answer's readability. You can also see a live demo here:
File app.js
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchMoviesSync, fetchMoviesAsyncMock } from "./api";
class App extends Component {
componentDidMount() {
this.props.fetchMoviesSync();
this.props.fetchMoviesAsyncMock();
}
render() {
return (
<div>
<div className="movies-root">
<div className="movies-wrapper">
<div className="movies-container safe-area">
{this.props.movies.join("\n")}
</div>
</div>
</div>
</div>
);
}
}
const mapStateToProps = (state) => ({ movies: state.movies });
const mapDispatchToProps = {
fetchMoviesSync,
fetchMoviesAsyncMock
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
File api.js
export const FETCHED_MOVIES = "FETCHED_MOVIES";
export const fetchMoviesSync = () => ({
type: FETCHED_MOVIES,
payload: ["movie1", "movie2", "movie3", "movie4"]
});
export const fetchMoviesAsyncMock = () => (dispatch) => {
dispatch({
type: FETCHED_MOVIES,
payload: ["movie5", "movie6", "movie7", "movie8"]
});
};
File reducer.js
const initialState = {
movies: [],
};
export default (state = initialState, action) => {
switch (action.type) {
case "FETCHED_MOVIES":
return {
...state,
movies: state.movies.concat(action.payload)
};
default:
return state;
}
};
File index.js
import React from "react";
import ReactDOM from "react-dom";
import Case from "./app";
import reducer from "./reducer";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import thunk from "redux-thunk";
let store = createStore(reducer, applyMiddleware(thunk));
ReactDOM.render(
<Provider store={store}>
<Case />
</Provider>,
document.getElementById("container")
);
File index.html
<body>
<div id="container"></div>
</body>
Hello I am using thunks to get data from my backend
but I am unsure how to do it in my combine reducer
my types:
export const FETCH_SUCESS = 'FETCH_SUCESS';
export const FETCH_FAIL = 'FETCH_FAIL';
export const FETCH_LOADING = 'FETCH_FAIL';
export const FILTER_PRODUCT = 'FILTER_PRODUCT';
my action:
import api from '../../services/api';
import {FETCH_SUCESS,FETCH_FAIL,FETCH_LOADING} from '../constants/fetchTypes';
const fetchSucess = data => ({
type: FETCH_SUCESS,
payload: {
...data
}
});
const fetchStarted = () => ({
type: FETCH_LOADING
});
const fetchFailed = error => ({
type: FETCH_FAIL,
payload: {
error
}
});
export const fetchProduct = () => {
console.log('action')
return dispatch => {
dispatch(fetchStarted());
api
.get('/products')
.then(res => {
dispatch(fetchSucess(res.data));
})
.catch(err => {
dispatch(fetchFailed(err.message));
});
};
};
my reducer:
import {
FETCH_SUCESS,
FETCH_FAIL,
FETCH_LOADING,
} from '../constants/fetchTypes';
const initialState = {
loading: false,
data: [],
error: null
};
export default function productReducer(state = initialState, action) {
switch (action.type) {
case FETCH_LOADING:
return {
...state,
loading: true
};
case FETCH_SUCESS:
return {
...state,
loading: false,
error: null,
data: [...state.data, action.payload]
};
case FETCH_FAIL:
return {
...state,
loading: false,
error: action.payload.error
};
default:
return state;
}
}
my combiner:
import { combineReducers } from 'redux'
import productReducer from './productsFetch.reducer';
export default combineReducers({
});
my store:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
export default function configureStore(initialState) {
return createStore(
rootReducer,
initialState,
applyMiddleware(thunk)
);
}
my home.js
class HomeProducts extends Component {
componentDidMount() {
this.props.fetchData();
}
render() {
const productItems = this.props.products.map( product => (
<div className="col-md-4 pt-4 pl-2">
<div className = "thumbnail text-center">
<a href={`#${product.id}`} onClick={(e)=>this.props.handleAddToCard(e,product)}>
<p>
{product.name}
</p>
</a>
</div>
<b>{util.formatCurrency(product.price)}</b>
<button className="btn btn-primary" onClick={(e)=>this.props.handleAddToCard(e,product)}>Add to Cart</button>
</div>
)
)
return (
<div className="container">
<div className="row">
{productItems}
</div>
</div>
)
}
}
const mapStateToProps = (state) => {
console.log(state);
};
const mapDispatchToProps = (dispatch) => {
return {
fetchData: () => dispatch(fetchProduct())
};
};
export default connect(mapStateToProps, mapDispatchToProps)(HomeProducts);
I have doubt what to use in my combiner
to get the date and the mistakes How I have my loading,data, error
I don't know how I will do it in meu combine redux
I also don't know if I had the best practices in my action and my reducer
In your combiner file just add your reducers as key value pairs like so:
import { combineReducers } from 'redux'
import productReducer from './productsFetch.reducer';
// import anotherReducer from './yourPath';
export default combineReducers({
products: productReducer,
// anotherState: anotherReducer
});
Ideally you should import your actions and pass it your component through your connect method like so then you will be able to access it from your component as props.
import fetchProduct from './pathToYourActionFile';
const mapStateToProps = (state) => {
console.log(state);
};
const mapActionsToProps = {
fetchProduct: fetchProduct
};
export default connect(mapStateToProps, mapActionsToProps)(HomeProducts);
import thunkInject from 'redux-thunk-inject';
const mockStore = configureMockStore([thunkInject()]);
const store = mockStore(mockStore);
const wrapper = mount(<Provider store={store} />);
expect(wrapper).toMatchSnapshot();
to mock a store with thunk, you can inject it as a prop in a component. Or in a reducer, e.g.
import productReducer from '../productReducer';
import {
FETCH_SUCESS,
FETCH_FAIL,
FETCH_LOADING,
} from '../constants/fetchTypes';
describe('product reducer', () => {
it('Should handle FETCH_SUCCESS', () => {
expect(productReducer(store, FETCH_SUCCESS)
).toEqual({
loading: true
});
expect(productReducer(store, FETCH_FAIL).toEqual({
loading: false,
error: action.payload.error})
});
I'm using React-Laravel for my project.
The problem is when I tried to use redux-thunk for the asynchronous dispatch function.
My dispatch function won't get executed.
Please do help me figure out this problem.
I have already tried to use promise or redux-devtools-extension library
https://codeburst.io/reactjs-app-with-laravel-restful-api-endpoint-part-2-aef12fe6db02
app.js
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import logger from 'redux-logger';
import Layout from './jsx/Layout/Layout';
import marketplaceReducer from './store/reducers/marketplace';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const appReducer = combineReducers({
marketplace: marketplaceReducer
});
const rootReducer = (state, action) => {
return appReducer(state, action);
}
const store = createStore(rootReducer, composeEnhancers(
applyMiddleware(logger, thunk)
));
const render = (
<Provider store={store}>
<BrowserRouter>
<Layout />
</BrowserRouter>
</Provider>
);
ReactDOM.render(render, document.getElementById('root'));
marketplace.js (action)
import * as actionTypes from './actionTypes';
import axios from '../../axios';
export const loadMarketplace = () => {
console.log("Load Marketplace");
return {
type: actionTypes.LOAD_MARKETPLACE
};
}
export const successMarketplace = (data) => {
console.log("Success Marketplace");
return {
type: actionTypes.SUCCESS_MARKETPLACE,
data: data
}
}
export const failedMarketplace = () => {
console.log("Failed Marketplace");
return {
type: actionTypes.FAILED_MARKETPLACE
}
}
export const showMarketplace = () => {
console.log("Show Marketplace Action")
return dispatch => {
//This is the problem
//Inside this function, I can't see any console.log, even loadMarketplace() didn't get called.
console.log("Show Marketplace in dispatch");
dispatch(loadMarketplace());
axios.get('/marketplaces')
.then(response => {
dispatch(successMarketplace(response));
})
.catch(error => {
dispatch(failedMarketplace());
});
};
}
marketplace.js (reducer)
import * as actionTypes from '../actions/actionTypes';
const initial_state = {
data: [],
loading: false
}
const loadMarketplace = (state, action) => {
console.log("Load Marketplace Reducer")
return {
...state,
loading: true
};
}
const successMarketplace = (state, action) => {
console.log("Success Marketplace Reducer", action.data)
return {
...state,
loading: false,
data: action.data
};
}
const failedMarketplace = (state, action) => {
return {
...state,
loading: false
};
}
const reducer = (state = initial_state, action) => {
//This is called when the first init, never got it through showMarketplace() function.
console.log("Marketplace Reducer", action);
switch (action.type) {
case actionTypes.LOAD_MARKETPLACE: return loadMarketplace(state, action);
case actionTypes.SUCCESS_MARKETPLACE: return successMarketplace(state, action);
case actionTypes.FAILED_MARKETPLACE: return failedMarketplace(state, action);
default: return state;
}
}
export default reducer;
Marketplace.js (jsx view)
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../../store/actions';
class Marketplace extends Component {
componentDidMount() {
console.log('[ComponentDidMount] Marketplace')
this.props.showMarketplace();
}
render() {
return (
<React.Fragment>
Marketplace
</React.Fragment>
);
}
}
const mapDispatchToProps = dispatch => {
return {
showMarketplace: () => dispatch(actions.showMarketplace)
};
}
export default connect(null, mapDispatchToProps)(Marketplace);
This is the result of my console.log (when loading the first time for Marketplace.js)
Please do help, I've been struggling for 2 hours or more, only because of this problem. (This is my first time using React-Laravel).
Thank you.
I already found the problem. It is not redux-thunk problem.
It is actually a normal Redux problem we found anywhere.
Marketplace.js (jsx view)
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../../store/actions';
class Marketplace extends Component {
componentDidMount() {
console.log('[ComponentDidMount] Marketplace')
this.props.showMarketplace();
}
render() {
return (
<React.Fragment>
Marketplace
</React.Fragment>
);
}
}
const mapDispatchToProps = dispatch => {
return {
showMarketplace: () => dispatch(actions.showMarketplace) //THIS IS THE PROBLEM, IT IS NOT EXECUTING PROPERLY. THIS ONE SHOULD BE
showMarketplace: () => dispatch(actions.showMarketplace()) //SHOULD BE LIKE THIS.
};
}
export default connect(null, mapDispatchToProps)(Marketplace);
Edited: I think it is something about thunk is not added right to redux.
First of all try to add only thunk.
const store = createStore(rootReducer, composeEnhancers(
applyMiddleware(thunk)
));
If it works, maybe try to change the order of them.
I'm new with Redux Thunk and I'm having problems with dispatch an action after fetching async call by click on button component.
actions.js
import fetch from 'isomorphic-fetch'
export const getPosts = (json) => {
return {
type: constant.GET_POSTS,
payload: {
data: json
}
}
}
export const loadPosts () => {
return (dispatch) => {
return fetch('https://jsonplaceholder.typicode.com/posts')
.then(res => {
res.json()
}).then(json => {
dispatch(getPosts(json))
})
}
}
button.js
class Button extends React.Component {
clicked(){
console.log(this.props.loadJsonPosts()) // got undefined here
}
render() {
return(
<button onClick={this.clicked.bind(this)}>click</button>
)
}
}
buttonContainer.js
import connect from 'react-redux/lib/components/connect'
import { loadPosts } from '../actions/actions.js'
import Button from '../components/Button'
function mapDispatchToProps(dispatch) {
return {
loadJsonPosts: () => { dispatch(loadPosts()) }
}
}
export default connect(null, mapDispatchToProps)(Button)
reducer.js
import * as constant from '../constants/index'
let initialState = { postList: [] }
const reducer = (state = initialState, action) => {
switch (action.type) {
case constant.GET_POSTS: //here i call my loadPosts action
state = Object.assign({}, { postList: [{ post: action.data }] })
break;
default:
break;
}
return state
}
export default reducer
App.jsx
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import Main from './components/Main'
import thunk from 'redux-thunk'
import { createStore, applyMiddleware } from 'redux'
import { Provider } from 'react-redux'
import reducer from './reducers/reducer'
const store = createStore(
reducer,
applyMiddleware(thunk)
)
class App extends Component {
render() {
return(
<Provider store={store}>
<Main />
</Provider>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('app')
)
I can't figure out why i get undefined, maybe I've missed something or I've wrong the approach
You forgot to return res.json() in actions.js for the next then block.
it should be
export const loadPosts () => {
return (dispatch) => {
return fetch('https://jsonplaceholder.typicode.com/posts')
.then(res => {
return res.json();
}).then(json => {
dispatch(getPosts(json))
})
}}
or you can skip the return statement by removing the blocks by writing .then(res => res.json())
I the same issue and found that ensuring the thunk middleware was first in my chain when creating the redux store allowed me to access the promise I was after rather than getting undefined,
store = createStore(
rootReducer,
initialState,
applyMiddleware(thunk, otherMiddleware1, otherMiddleware2)
);
mapDispatchToProps should be like this:
function mapDispatchToProps(dispatch) {
return {
// loadPosts instead of loadPosts()
loadJsonPosts: () => { dispatch(loadPosts) }
} }