Redux: Melhor técnica de carregamento assíncrono do lado do servidor?

Criado em 15 jun. 2015  ·  63Comentários  ·  Fonte: reduxjs/redux

Em primeiro lugar, adoro esta biblioteca e os padrões que vocês estão usando. 👍👍

Estou tentando usar o redux para construir um aplicativo isomórfico. Está funcionando perfeitamente até agora, exceto que preciso descobrir como esperar até que minhas lojas sejam carregadas (no servidor) antes de retornar o carregamento inicial da página. Idealmente, o carregamento deve ocorrer nas próprias lojas, mas quando eu chamo dispatch(userActions.load()) , a loja deve retornar o novo estado (ou seja, return { ...state, loading: true }; ), por isso não pode retornar uma promessa para Esperar por. dispatch() retorna a ação passada a ele por algum motivo. Eu realmente gostaria de algo como ...

dispatch(someAsyncAction, successAction, failureAction) => Promise

... onde a promessa não se resolve até que uma das outras duas ações seja despachada.

É esse o tipo de coisa que pode ser habilitada com o padrão de middleware?

Estou totalmente enganado e já existe uma maneira simples de fazer isso?

Obrigado.

Comentários muito úteis

// Middleware
export default function promiseMiddleware() {
  return (next) => (action) => {
    const { promise, types, ...rest } = action;
    if (!promise) {
      return next(action);
    }

    const [REQUEST, SUCCESS, FAILURE] = types;
    next({ ...rest, type: REQUEST });
    return promise.then(
      (result) => next({ ...rest, result, type: SUCCESS }),
      (error) => next({ ...rest, error, type: FAILURE })
    );
  };
}

// Usage
function doSomethingAsync(userId) {
  return {
    types: [SOMETHING_REQUEST, SOMETHING_SUCCESS, SOMETHING_FAILURE],
    promise: requestSomething(userId),
    userId
  };
}

Todos 63 comentários

Ei, obrigado!

Idealmente, o carregamento deve ocorrer nas próprias lojas

Redux garante que os Stores sejam completamente síncronos. O que você descreve deve acontecer no criador da ação.

_Acho_ que pode até ser possível com o middleware thunk padrão. Seu criador de ação seria assim:

export function doSomethingAsync() {
  return (dispatch) => {
    dispatch({ type: SOMETHING_STARTED });

    return requestSomething().then(
      (result) =>  dispatch({ type: SOMETHING_COMPLETED, result }),
      (error) =>  dispatch({ type: SOMETHING_FAILED, error })
    );
  };
}

e lidar com as ações reais (granulares) na Loja.

Também é possível escrever um middleware personalizado para remover o clichê.

Gênio! Achei que estava negligenciando algo óbvio. Eu gosto dessa separação de _fazer_ e _armazenar_.

Estou ansioso para ver esta biblioteca crescer, embora ela já esteja bastante completa. Saúde, @gaearon!

Você também pode escrever um middleware personalizado como este

export default function promiseMiddleware() {
  return (next) => (action) => {
    const { promise, ...rest } = action;
    if (!promise) {
      return next(action);
    }

    next({ ...rest, readyState: 'request' );
    return promise.then(
      (result) => next({ ...rest, result, readyState: 'success' }),
      (error) => next({ ...rest, error, readyState: 'failure' })
    );
  };
}

e use-o em vez do padrão.

Isso permitirá que você escreva criadores de ação assíncrona como

function doSomethingAsync(userId) {
  return {
    type: SOMETHING,
    promise: requestSomething(userId),
    userId
  };
}

e transformá-los em

{ type: SOMETHING, userId: 2, readyState: 'request' }
{ type: SOMETHING, userId: 2, readyState: 'success' }
{ type: SOMETHING, userId: 2, readyState: 'failure' }

Ooh, isso também é bom, e mais do que eu tinha em mente quando fiz a pergunta original. Não posso dizer se gosto da compensação em reduzir o número de constantes de ação em troca de adicionar if s para verificar o readyState dentro da loja. Acho que prefiro ter _SUCCESS e _FAILURE versões adicionais de cada ação apenas para evitar colocar if dentro de case .

Obrigado, no entanto.

Sim, depende totalmente do seu gosto. Você poderia ter uma versão semelhante que transforma types: { request: ..., success: ..., failure: ... } em ações. Este é o objetivo de torná-lo um middleware em vez de assá-lo na biblioteca: todo mundo tem seu próprio gosto para essas coisas.

// Middleware
export default function promiseMiddleware() {
  return (next) => (action) => {
    const { promise, types, ...rest } = action;
    if (!promise) {
      return next(action);
    }

    const [REQUEST, SUCCESS, FAILURE] = types;
    next({ ...rest, type: REQUEST });
    return promise.then(
      (result) => next({ ...rest, result, type: SUCCESS }),
      (error) => next({ ...rest, error, type: FAILURE })
    );
  };
}

// Usage
function doSomethingAsync(userId) {
  return {
    types: [SOMETHING_REQUEST, SOMETHING_SUCCESS, SOMETHING_FAILURE],
    promise: requestSomething(userId),
    userId
  };
}

Oh cara, eu amo essa solução. Muito mais agradável do que ter then() e chamadas adicionais para dispatch() como a primeira solução que você propôs. Viva o middleware!

Deixe-me saber como (e se ;-) funciona!
Na verdade, não testamos muito o middleware personalizado.

Você omitiu } (isso é -1 ponto 😀), mas funcionou perfeitamente! Primeira vez.

: +1:

@erikras estou

É apenas um pseudocódigo, então não cole isso em lugar nenhum, mas estou usando o react-router (cuja api está mudando tão rápido quanto a do redux) algo assim:

app.get('/my-app', (req, res) => {
  Router.run(routes, req.path, (error, initialState) => {
    Promise.all(initialState.components
      .filter(component => component.fetchData) // only components with a static fetchData()
      .map(component => {
        // have each component dispatch load actions that return promises
        return component.fetchData(redux.dispatch);
      })) // Promise.all combines all the promises into one
      .then(() => {
        // now fetchData() has been run on every component in my route, and the
        // promises resolved, so we know the redux state is populated
        res.send(generatePage(redux));
      });
  });
});

Isso esclarece alguma coisa?

@iest

Citando seu problema no Slack:

Eu tenho um gerenciador de rota com

 static async routerWillRun({dispatch}) {
   return await dispatch(UserActions.fooBar());
 }

onde UserActions.fooBar() é:

export function fooBar() {
 return dispatch => {
   doAsync().then(() => dispatch({type: FOO_BAR}));
 };
}

então, na renderização do servidor estou produzindo:

 yield myHandler.routerWillRun({dispatch: redux.dispatch});

mas não funciona.

Acho que o problema aqui é que você não está retornando nada do método aninhado de fooBar .

Remova as chaves:

export function fooBar() {
  return dispatch =>
    doAsync().then(() => dispatch({type: FOO_BAR}));
}

ou adicione uma instrução return explícita:

export function fooBar() {
  return dispatch => {
    return doAsync().then(() => dispatch({type: FOO_BAR}));
  };
}

De qualquer forma, pode ser mais fácil usar um middleware de promessa personalizado, conforme sugerido acima.

@erikras Com relação ao seu último comentário onde você está chamando o método fetchData no que você tem como initialState.components (no retorno de chamada de Router.run), o objeto do qual você obtém as referências do componente retorna apenas os manipuladores de rota correspondentes. O que você acha de alcançar componentes que podem não ser um manipulador de rota correspondente, ou seja, um componente filho, mas precisa buscar dados?

aqui está um exemplo do que estou falando

import React from 'react';
import Router from 'react-router';
import {Route, RouteHandler, DefaultRoute} from 'react-router';

//imagine Bar needs some data
const Bar = React.createClass({
  render(){
    return(
      <div>bar</div>);
  }
});

const Foo = React.createClass({
  render(){
    return (
      <div>
        foo
        <Bar/>
      </div>);
  }
});


const App = React.createClass({
  render(){
    return (
      <div>
        <RouteHandler />
      </div>
    );
  }
});

const routes = (
  <Route path="/" handler={App} name="App">
    <DefaultRoute handler={Foo} name="Foo"/>
  </Route>
);

Router.run(routes,'/',function(Root,state){
  console.log(state);
});

saída:

{ path: '/',
  action: null,
  pathname: '/',
  routes: 
   [ { name: 'App',
       path: '/',
       paramNames: [],
       ignoreScrollBehavior: false,
       isDefault: false,
       isNotFound: false,
       onEnter: undefined,
       onLeave: undefined,
       handler: [Object],
       defaultRoute: [Object],
       childRoutes: [Object] },
     { name: 'Foo',
       path: '/',
       paramNames: [],
       ignoreScrollBehavior: false,
       isDefault: true,
       isNotFound: false,
       onEnter: undefined,
       onLeave: undefined,
       handler: [Object] } ],
  params: {},
  query: {} }

Você não terá acesso ao Bar in Routes

@erikras Fantastic! Esse é exatamente o tipo de rota que desejo seguir. Obrigado por compartilhar.

@iest Espero que o trocadilho tenha sido intencional, "siga um caminho" iterando pelas rotas correspondentes. :-)

@mattybow Isso é verdade. Se você _realmente_ precisa de um componente que não está em suas rotas para carregar algo, a única opção é executar React.renderToString() uma vez (descartando o resultado), fazer todo o carregamento em componentWillMount() e de alguma forma salve as promessas conforme você avança. Isso é o que eu estava fazendo com minha própria solução de roteamento desenvolvida internamente antes da renderização do lado do servidor com suporte para react-router . Eu sugeriria que a necessidade de componentes que não são de rota para fazer o carregamento pode ser um sintoma de um problema de design. Na maioria dos casos de uso, uma rota sabe quais dados seus componentes precisarão.

@erikras
você tem algum repo público para ver um exemplo completo de sua solução lá?

@transedward Eu gostaria de ter feito isso, mas minhas coisas até agora usando o método que

+1 no exemplo isomórfico avançado
Eu amo aonde isso vai dar!

@transedward Aqui está um projeto de amostra com toda a tecnologia de ponta que construí. https://github.com/erikras/react-redux-universal-hot-example/

@erikras Isso é incrível! Você pode enviar PR para adicioná-lo à seção de "kits iniciais" da documentação do README e do React Hot Loader?

Obrigado! PRs enviados.

@erikras Ótimo - obrigado!

Apenas uma observação que - com base em algumas idéias desta conversa - fiz um middleware para lidar com promessas: https://github.com/pburtchaell/redux-promise-middleware.

@pburtchaell Também existe esta biblioteca de @acdlite . https://github.com/acdlite/redux-promise

Duas reflexões sobre isso:

  1. As promessas que são convertidas em ações podem ser repassadas junto com a ação e colocadas na Loja Redux.

Então, para saber se sua página está pronta para renderizar, basta verificar se todas as promessas foram cumpridas. Talvez use um middleware que reúna todas as promessas passadas e forneça uma promessa para quando não houver promessas pendentes.

  1. Que tal permitir que os seletores chamem ações para dados ausentes?

Suponha que você queira renderizar a mensagem 3, então seu contêiner de mensagem renderizará <Message id={3}> e o seletor de mensagem verificará se state.msgs[3] existe e, se não, enviará uma promessa de carregamento de mensagem.

Portanto, combinar os dois e os componentes selecionariam automaticamente os dados necessários e você saberia quando eles estivessem prontos.

Tenho certeza de que “não coloque nada não serializável na loja ou nas ações”. É uma das invariantes que tem funcionado muito bem para mim (e permitia viagens no tempo, por exemplo) e deve haver _muito_ razões convincentes para considerar alterá-la.

Então, para saber se sua página está pronta para renderizar, basta verificar se todas as promessas foram cumpridas. Talvez use um middleware que reúna todas as promessas passadas e forneça uma promessa para quando não houver promessas pendentes.

Na verdade, isso não exige colocar promessas na loja no final, e eu gosto disso. A diferença é que, no final da cadeia de despacho, as ações brutas não contêm nenhuma promessa. Em vez disso, eles são “coletados” pelo referido middleware.

Uma coisa que geralmente faço quando trabalho com promessas é manter uma referência a uma promessa por perto, de modo que, quando outras solicitações para a mesma coisa vierem, eu simplesmente retorne a mesma promessa, fornecendo compensação. Em seguida, excluo a referência algum tempo após o cumprimento da promessa, fornecendo armazenamento em cache configurável.

Eu realmente tenho que começar a usar o Redux em aplicativos reais, porque me pergunto o que fazer com essas referências no Redux. Eu meio que quero colocá-los na loja para tornar o actionCreator sem estado (ou pelo menos tornando o estado explícito). Passar a promessa por meio de uma ação é uma boa maneira de exportá-la, mas você precisará recuperá-la posteriormente de alguma forma. Hmmm.

Estou realmente ansioso para responder ao último ponto de @wmertens .
Evitar várias chamadas simultâneas (não fazer nada se algo estiver pendente) é um caso de uso frequente, não tenho certeza de qual é a melhor maneira de responder a isso (porque você não pode acessar o estado da loja a partir de um actionCreator).

O usuário actionCreator (o componente) deve verificar todas as vezes no estado se ele pode ou não despachar a ação? Você tem que fazer isso sempre, então ... Talvez você precise introduzir sua própria anotação @connect que envolva isso?

porque você não pode acessar o estado da loja de um actionCreator

Porque? Você pode fazer isso muito bem se usar middleware thunk .

function doSomething() {
  return { type: 'SOMETHING' };
}

function maybeDoSomething() {
  return function (dispatch, getState) {
    if (getState().isFetching) {
      return;
    }

    dispatch(doSomething());
  };
}

store.dispatch(maybeDoSomething());

isso resolve tudo!
Eu pensei por um tempo (sem motivo) que acessar o estado em actionCreator era uma prática ruim ^^ já que o criador de ação pode acessar o estado, não vejo por que o actionCreator não deveria, então é legal, podemos fazer isso :)

Obrigado @gaearon

@gaearon , é esta técnica de usar Thunk e ações distintas http://gaearon.github.io/redux/docs/api/applyMiddleware.html preferível à sua resposta acima:

Você também pode escrever um middleware personalizado como este

export default function promiseMiddleware() {
  return (next) => (action) => {
    const { promise, ...rest } = action;
    if (!promise) {
      return next(action);
    }

    next({ ...rest, readyState: 'request' );
    return promise.then(
      (result) => next({ ...rest, result, readyState: 'success' }),
      (error) => next({ ...rest, error, readyState: 'failure' })
    );
  };
}

e use-o em vez do padrão.
Isso permitirá que você escreva criadores de ação assíncrona como

function doSomethingAsync(userId) {
  return {
    type: SOMETHING,
    promise: requestSomething(userId),
    userId
  };
}

e transformá-los em

{ type: SOMETHING, userId: 2, readyState: 'request' }
{ type: SOMETHING, userId: 2, readyState: 'success' }
{ type: SOMETHING, userId: 2, readyState: 'failure' }

Também,

Acho que para a última parte, você quis dizer:

{ type: SOMETHING, userId: 2, readyState: 'request' }
{ type: SOMETHING, userId: 2, result, readyState: 'success' }
{ type: SOMETHING, userId: 2, error, readyState: 'failure' }

Estou supondo que só depende se alguém deseja criar ações separadas para os retornos de chamada success ou failure da promessa, em vez de usar o gerado automaticamente.

Em seu exemplo thunk:

function makeASandwichWithSecretSauce(forPerson) {

  // Invert control!
  // Return a function that accepts `dispatch` so we can dispatch later.
  // Thunk middleware knows how to turn thunk async actions into actions.

  return function (dispatch) {
    return fetchSecretSauce().then(
      sauce => dispatch(makeASandwich(forPerson, sauce)),
      error => dispatch(apologize('The Sandwich Shop', forPerson, error))
    );
  };
}

então, não faria necessariamente sentido ter um retorno de chamada de erro genérico para uma falha ao buscar um molho secreto, pois pode haver circunstâncias diferentes para buscar o molho.

Portanto, posso ver o modelo thunk um pouco mais flexível.

Possivelmente algo como registro ou talvez até mesmo a alternância de um "indicador de ocupado assíncrono em andamento" são exemplos mais apropriados de middleware?

@ justin808

Ambos estão bem. Escolha o que é menos detalhado para você e o que funciona melhor para o seu projeto. Minha sugestão é começar usando thunks e, se você vir algum padrão se repetindo, extraia-o para um middleware personalizado. Você pode misturá-los também.

Criei um ActionStore para separar o estado das ações disparadas (carregamento, êxito, falha) do outro estado. Mas não sei se isso vai contra a base do Redux / Flux. Eu postei no stackoverflow sobre isso.

@gabrielgiussi Acho que https://github.com/acdlite/redux-promise também pode conseguir o que você deseja sem você ter que armazenar promessas no estado. O estado deve ser serializável o tempo todo.

@wmertens obrigado pelo conselho. Vou dar uma olhada no repo, mas por que meu estado não seria serializável? Ou você diz isso apenas para eu tomar nota?

@gabrielgiussi Eu não olhei muito de perto, mas parecia que você estava
colocar promessas ou funções na loja. Em qualquer caso, esse projeto
também deve funcionar bem, eu acho.

Na segunda-feira, 10 de agosto de 2015, 19:15, gabrielgiussi [email protected] escreveu:

@wmertens https://github.com/wmertens obrigado pelo conselho. eu vou
dê uma olhada no repo, mas por que meu estado não seria serializável? Ou você
dizer isso apenas para eu tomar nota?

-
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/gaearon/redux/issues/99#issuecomment -129531103.

Wout.
(digitado no celular, desculpe concisão)

Na verdade, na loja eu coloquei objetos Action personalizados, eles são apenas Immutable.Record com atributos simples (id, state, payload) e um gatilho de ação que cria e retorna uma Promise, então não estou colocando Promises na Store. Mas provavelmente estou quebrando algo em outro lugar, je. Obrigado @wmertens.

@gabrielgiussi

e um gatilho de ação que cria e retorna uma promessa

Não coloque funções ou qualquer outra coisa que não seja serializável no estado.

Desculpa. Eu tentei dizer

e um gatilho de função que cria e retorna uma promessa

O que estou colocando na loja é um objeto Action (o nome não era o melhor):

export default class Action extends Immutable.Record({state: 'idle', api: null, type: null, payload: null, id: null}){
load(){
  return this.set('state','loading');
}

succeed(){
  return this.set('state','succeeded');
}

fail(){
  return this.set('state','failed');
}

ended(){
  return this.get('state') != 'loading' && this.get('state') != 'idle';
}

endedWithSuccess(){
  return this.get('state') == 'succeeded';
}

endedWithFailure(){
  return this.get('state') == 'failed';
}

trigger() {
  return (dispatch) => {
    dispatch({type: this.get('type') + '_START', action: this});
    let payload = this.get('payload');
    this.get('api').call({dispatch,payload}).then((result) => {
      dispatch({type: this.get('type') + '_SUCCESS',id: this.get('id'), result: result.result});
    }).catch((result) => {
        dispatch({type: this.get('type') + '_FAIL',id: this.get('id'), result: result.result});
      });
  }
}
}

Eu criei uma biblioteca para resolver este problema (veja # 539) que funciona fazendo com que o middleware retorne promessas para ações pendentes e esperando que todas essas promessas sejam resolvidas.

@gaearon este código que você escreveu https://github.com/rackt/redux/issues/99#issuecomment -112212639,

Isso é algo que está incluído na biblioteca redux ou é algo que tenho que criar manualmente? Desculpe se esta é uma questão newb, apenas entrando em React / Flux (Redux). Acabei de começar este tutorial https://github.com/happypoulp/redux-tutorial

@ banderson5144

Não está incluído. Ele está lá apenas para lhe dar uma ideia do que você pode fazer - mas você é livre para fazer de forma diferente.
Algo semelhante foi publicado como https://github.com/pburtchaell/redux-promise-middleware.

Obrigado por esta informação útil. Eu queria escolher seu cérebro em redefinir uma loja -

  • Você redefiniu o estado da loja para um novo usuário
  • Você espera que algumas ações assíncronas sejam concluídas antes de fornecer ao usuário a loja e o html
  • Uma ação assíncrona em execução anterior para outro usuário é concluída e sua loja fica poluída

Como vocês resolveram isso / têm alguma ideia de como? Apenas uma nova loja para cada usuário funcionaria?

Você está falando sobre renderização de servidor? Crie uma nova loja a cada solicitação. Temos um guia para renderização de servidor nos documentos.

Obrigado eu farei isso

Depois de tentar entender ...

Isso é muito ingênuo? (ninguém mais parece fazer isso - eu acho)

// server.js
app.use(function (req, res) {
    match({…}, function (error, redirectLocation, renderProps) {
        …

        if (renderProps) {
            const store = configureStore();

            const promises = renderProps.components.map(function (component, index) {
                if (typeof component.fetchData !== 'function') {
                    return false;
                }

                return component.fetchData(store.dispatch);
            });

            Promise.all(promises).then(function () {
                res.status(200).send(getMarkup(store, renderProps));
            });
        }
    })
});
// home.js
export class Home extends Component {
    static fetchData() {
        return Promise.all([
            dispatch(asyncAction);
        ]);
    },

    componentDidMount() {
        const { dispatch } = this.props;

        Home.fetchData(dispatch);
    }
}

export default connect()(Home);
// action.js
export function asyncAction() {
    return (dispatch, getState) => {
        dispatch(request);

        return fetch(…)
            .then(response => response.json())
            .then(data => dispatch(requestSuccess(data)))
        ;
    }
}

Eu também estava tentando descobrir uma solução para @mattybow 's pergunta https://github.com/rackt/redux/issues/99#issuecomment -112980776 (componentes aninhados gerenciamento de dados DNS), mas não há tal sucesso (não foi com certeza sobre como coletar promessas de componentWillMount ).

@chemoish Também estou tentando entender a renderização do lado do servidor com react e redux. O exemplo na documentação não trata muito bem esse caso de uso. Não quero especificar e acoplar cada solicitação de API no servidor com meus componentes novamente. O componente só deve especificar como obter os dados necessários (que o servidor deve coletar).

Suas soluções parecem muito boas para fazer isso. Isso funcionou para você? Obrigado

Edit: Estou certo de que "componentDidMount" não é acionado novamente no cliente quando é processado no servidor?

@ ms88privat Não recebi muito feedback sobre a solução ainda, nem testei seus limites.

No entanto, a solução proposta acima requer que cada página conheça os dados de todos os seus componentes filhos. Não me aprofundei em como os componentes aninhados se preocupam com o gerenciamento de dados em si (devido à coleta de promessas aninhadas).

Parece estar fazendo o que você esperaria, então é bom o suficiente para mim por enquanto.


componentDidMount será acionado novamente (consulte https://facebook.github.io/react/docs/component-specs.html#mounting-componentdidmount). Você pode usar este ou outro método de ciclo de vida que atenda às suas necessidades.

Eu consigo contornar isso evitando que o código fetch seja executado se a loja já estiver cheia (ou qualquer lógica de negócios que você achar adequada).

Examine https://github.com/reactjs/redux/blob/master/examples/async/actions/index.js#L47 para ter uma ideia do que estou falando.

@chemoish

Eu consigo contornar isso evitando que o código de busca seja executado se o armazenamento já estiver cheio

Ok, eu cuido disso. Obrigado.

No entanto, a solução proposta acima requer que cada página conheça os dados de todos os seus componentes filhos.

Talvez eu tenha interpretado mal sua solução, mas isso não é um requisito necessário, independentemente da renderização do lado do servidor? (por exemplo, deve renderizar o mesmo estado, se eu atualizar na rota atual, mesmo que seja um SPA)

Sim, mas você pode querer que um componente aninhado gerencie sua própria busca de dados, por qualquer motivo.

Por exemplo, um componente que se repete em muitas páginas, mas cada página não tem muitas necessidades de busca de dados.

@chemoish Não tenho certeza se estamos na mesma página. Deixe-me tentar explicar qual é o meu ponto de vista.

Por exemplo, eu tenho três componentes aninhados:

  • component1 (static dataFetch1)

    • component2 (estático dataFetch2)

    • component3 (static dataFetch3)

Cada um deles tem seus próprios métodos "componentDidMount", com suas próprias declarações dataFetching (ações de despacho por meio de seu método estático dataFetching).

Se eu não tiver a renderização do lado do servidor e atualizar o URL atual, meus componentes irão montar e acionar todas as ações necessárias para carregar todos os dados necessários posteriormente.

Com a renderização do lado do servidor, sua função match e renderProps extrairão todos os três componentes, para que eu possa acessar todos os métodos estáticos de busca de dados, que me permitirão buscar todos os dados necessários para a renderização inicial do lado do servidor?

Você tem uma referência ao seu match function no exemplo fornecido? Valeu.

@ ms88privat renderProps.components é um conjunto de componentes do roteador, não é mais profundo do que isso. @chemoish significava que, com sua implementação, você não pode descrever as necessidades de busca de dados em componentes mais profundos.

@DominicTobias thx, você tem uma solução para este problema? Existe a possibilidade de obter todos os componentes aninhados?

Provavelmente isso pode ajudar? https://github.com/gaearon/react-side-effect
Usado para coletar todas as metatags de elementos aninhados: https://github.com/nfl/react-helmet

Desculpe por interromper esta discussão novamente, mas recentemente encontrei o mesmo problema de estado de pré-preenchimento com ação assíncrona.

Posso ver que @erikras mudou seu projeto clichê para redux-async-connect . Será que alguém encontrou outra solução?

@vtambourine Estive olhando https://github.com/markdalgleish/redial que é bastante útil

Sim, eu olhei através dele. Mas não entendi como garantir que os dados
a busca conectada não será executada uma segunda vez após o código de reinicialização para n
cliente.
Em Пт, 18 de марта 2016 г. às 22:54, Sean Matheson [email protected]
escreveu:

@vtambourine https://github.com/vtambourine que estive olhando
https://github.com/markdalgleish/redial que é bastante útil

-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/reactjs/redux/issues/99#issuecomment -198517067

Também curioso se alguém encontrou uma solução estável para este desafio. Eu amo o boilerplate do @erikras , mas como @vtambourine mencionou, ele mudou para redux-async-connect, o que parece que pode não ser uma solução estável de longo prazo: # 81 Redux-async-connect está morto? .

@vtambourine existe um fork que está disponível em https://github.com/makeomatic/redux-connect e é bem mantido. Possui API semelhante com algumas alterações, verifique se você estiver interessado

para aqueles interessados ​​em uma solução redux com middleware como mencionado por @gaearon, eu tenho um projeto de exemplo que implementa essa técnica e permite que os próprios componentes solicitem os dados de que precisam no lado do servidor

https://github.com/peter-mouland/react-lego-2016#redux -with-promessa-middleware

Como fazer o teste de unidade do criador de ação com essa abordagem?

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

captbaritone picture captbaritone  ·  3Comentários

cloudfroster picture cloudfroster  ·  3Comentários

ramakay picture ramakay  ·  3Comentários

CellOcean picture CellOcean  ·  3Comentários

timdorr picture timdorr  ·  3Comentários